2025-01-06 00:12:03 +00:00
|
|
|
# typed: strict
|
2019-04-19 15:38:03 +09:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2020-10-10 15:04:46 +02:00
|
|
|
require "utils/string_inreplace_extension"
|
|
|
|
|
2013-07-11 21:55:02 -05:00
|
|
|
module Utils
|
2020-08-19 08:18:14 +02:00
|
|
|
# Helper functions for replacing text in files in-place.
|
|
|
|
module Inreplace
|
2022-07-30 19:10:40 +02:00
|
|
|
# Error during text replacement.
|
2020-08-19 08:18:14 +02:00
|
|
|
class Error < RuntimeError
|
2025-01-06 00:12:03 +00:00
|
|
|
sig { params(errors: T::Hash[String, T::Array[String]]).void }
|
2020-08-19 08:18:14 +02:00
|
|
|
def initialize(errors)
|
|
|
|
formatted_errors = errors.reduce(+"inreplace failed\n") do |s, (path, errs)|
|
|
|
|
s << "#{path}:\n" << errs.map { |e| " #{e}\n" }.join
|
|
|
|
end
|
|
|
|
super formatted_errors.freeze
|
2016-09-11 17:47:04 +01:00
|
|
|
end
|
2014-09-28 01:08:31 -05:00
|
|
|
end
|
|
|
|
|
2015-08-29 10:56:24 +01:00
|
|
|
# Sometimes we have to change a bit before we install. Mostly we
|
2021-01-09 13:34:17 +11:00
|
|
|
# prefer a patch, but if you need the {Formula#prefix prefix} of
|
|
|
|
# this formula in the patch you have to resort to `inreplace`,
|
|
|
|
# because in the patch you don't have access to any variables
|
|
|
|
# defined by the formula, as only `HOMEBREW_PREFIX` is available
|
|
|
|
# in the {DATAPatch embedded patch}.
|
2018-10-18 21:42:43 -04:00
|
|
|
#
|
2024-04-26 20:55:51 +02:00
|
|
|
# ### Examples
|
2020-08-19 08:18:14 +02:00
|
|
|
#
|
2024-04-26 20:55:51 +02:00
|
|
|
# `inreplace` supports regular expressions:
|
|
|
|
#
|
|
|
|
# ```ruby
|
|
|
|
# inreplace "somefile.cfg", /look[for]what?/, "replace by #{bin}/tool"
|
|
|
|
# ```
|
|
|
|
#
|
|
|
|
# `inreplace` supports blocks:
|
|
|
|
#
|
|
|
|
# ```ruby
|
|
|
|
# inreplace "Makefile" do |s|
|
|
|
|
# s.gsub! "/usr/local", HOMEBREW_PREFIX.to_s
|
|
|
|
# end
|
|
|
|
# ```
|
2021-01-09 13:34:17 +11:00
|
|
|
#
|
|
|
|
# @see StringInreplaceExtension
|
2020-08-19 08:18:14 +02:00
|
|
|
# @api public
|
2021-01-17 22:45:55 -08:00
|
|
|
sig {
|
2020-10-10 15:04:46 +02:00
|
|
|
params(
|
2023-08-01 09:32:42 -07:00
|
|
|
paths: T.any(T::Enumerable[T.any(String, Pathname)], String, Pathname),
|
2023-07-23 18:23:04 +08:00
|
|
|
before: T.nilable(T.any(Pathname, Regexp, String)),
|
2023-07-21 19:48:28 -07:00
|
|
|
after: T.nilable(T.any(Pathname, String, Symbol)),
|
2020-10-10 15:04:46 +02:00
|
|
|
audit_result: T::Boolean,
|
2024-10-23 12:20:24 -04:00
|
|
|
global: T::Boolean,
|
2023-08-07 17:36:13 -07:00
|
|
|
block: T.nilable(T.proc.params(s: StringInreplaceExtension).void),
|
2020-10-10 15:04:46 +02:00
|
|
|
).void
|
2021-01-17 22:45:55 -08:00
|
|
|
}
|
2024-10-23 12:20:24 -04:00
|
|
|
def self.inreplace(paths, before = nil, after = nil, audit_result: true, global: true, &block)
|
2023-08-01 09:32:42 -07:00
|
|
|
paths = Array(paths)
|
2023-07-21 19:48:28 -07:00
|
|
|
after &&= after.to_s
|
2023-07-23 15:56:26 -07:00
|
|
|
before = before.to_s if before.is_a?(Pathname)
|
2020-09-19 01:53:59 +02:00
|
|
|
|
2014-09-28 01:08:31 -05:00
|
|
|
errors = {}
|
|
|
|
|
2023-08-01 09:32:42 -07:00
|
|
|
errors["`paths` (first) parameter"] = ["`paths` was empty"] if paths.all?(&:blank?)
|
2019-11-20 19:10:15 +00:00
|
|
|
|
2023-08-01 09:32:42 -07:00
|
|
|
paths.each do |path|
|
2021-12-23 14:58:51 -05:00
|
|
|
str = File.binread(path)
|
2020-07-25 00:48:15 +05:30
|
|
|
s = StringInreplaceExtension.new(str)
|
2013-07-11 21:55:02 -05:00
|
|
|
|
|
|
|
if before.nil? && after.nil?
|
2023-08-07 17:26:46 -07:00
|
|
|
raise ArgumentError, "Must supply a block or before/after params" unless block
|
2023-08-04 16:18:54 -07:00
|
|
|
|
2014-09-28 01:08:31 -05:00
|
|
|
yield s
|
2024-10-23 12:20:24 -04:00
|
|
|
elsif global
|
2024-09-24 10:15:34 +01:00
|
|
|
s.gsub!(T.must(before), T.must(after), audit_result:)
|
2024-10-23 12:20:24 -04:00
|
|
|
else
|
|
|
|
s.sub!(T.must(before), T.must(after), audit_result:)
|
2013-07-11 21:55:02 -05:00
|
|
|
end
|
|
|
|
|
2016-08-05 22:01:32 +08:00
|
|
|
errors[path] = s.errors unless s.errors.empty?
|
2014-09-28 01:08:31 -05:00
|
|
|
|
2020-07-25 00:48:15 +05:30
|
|
|
Pathname(path).atomic_write(s.inreplace_string)
|
2013-07-11 21:55:02 -05:00
|
|
|
end
|
2014-09-28 01:08:31 -05:00
|
|
|
|
2022-08-08 16:18:06 +02:00
|
|
|
raise Utils::Inreplace::Error, errors if errors.present?
|
2013-07-11 21:55:02 -05:00
|
|
|
end
|
2020-08-16 10:28:26 -07:00
|
|
|
|
2025-01-06 00:12:03 +00:00
|
|
|
sig {
|
|
|
|
params(
|
|
|
|
path: T.any(String, Pathname),
|
|
|
|
replacement_pairs: T::Array[[T.any(Regexp, Pathname, String), T.any(Pathname, String)]],
|
|
|
|
read_only_run: T::Boolean,
|
|
|
|
silent: T::Boolean,
|
|
|
|
).returns(String)
|
|
|
|
}
|
2023-08-04 16:20:38 -07:00
|
|
|
def self.inreplace_pairs(path, replacement_pairs, read_only_run: false, silent: false)
|
2021-12-23 14:58:51 -05:00
|
|
|
str = File.binread(path)
|
2020-08-16 10:28:26 -07:00
|
|
|
contents = StringInreplaceExtension.new(str)
|
|
|
|
replacement_pairs.each do |old, new|
|
2025-01-06 00:12:03 +00:00
|
|
|
if old.blank?
|
2020-08-16 10:28:26 -07:00
|
|
|
contents.errors << "No old value for new value #{new}! Did you pass the wrong arguments?"
|
|
|
|
next
|
|
|
|
end
|
|
|
|
|
|
|
|
contents.gsub!(old, new)
|
|
|
|
end
|
2022-08-08 16:18:06 +02:00
|
|
|
raise Utils::Inreplace::Error, path => contents.errors if contents.errors.present?
|
2020-08-16 10:28:26 -07:00
|
|
|
|
|
|
|
Pathname(path).atomic_write(contents.inreplace_string) unless read_only_run
|
|
|
|
contents.inreplace_string
|
|
|
|
end
|
2013-07-11 21:55:02 -05:00
|
|
|
end
|
|
|
|
end
|