diff --git a/Library/Homebrew/cli/args.rb b/Library/Homebrew/cli/args.rb index e74a3d8b57b72f..0c3cd0b7d11829 100644 --- a/Library/Homebrew/cli/args.rb +++ b/Library/Homebrew/cli/args.rb @@ -14,6 +14,9 @@ class Args sig { returns(T::Array[String]) } attr_reader :options_only, :flags_only, :remaining + sig { returns(T::Hash[Symbol, T.any(NilClass, T::Boolean, String, T::Array[String])]) } + attr_accessor :table + sig { void } def initialize require "cli/named_args" @@ -23,7 +26,7 @@ def initialize @options_only = T.let([], T::Array[String]) @flags_only = T.let([], T::Array[String]) @cask_options = T.let(false, T::Boolean) - @table = T.let({}, T::Hash[Symbol, T.untyped]) + @table = T.let({}, T::Hash[Symbol, T.any(NilClass, T::Boolean, String, T::Array[String])]) # Can set these because they will be overwritten by freeze_named_args! # (whereas other values below will only be overwritten if passed). @@ -55,15 +58,9 @@ def build_bottle? = false sig { returns(T::Boolean) } def build_from_source? = false - sig { returns(T::Boolean) } - def cask? = false - sig { returns(T::Boolean) } def force_bottle? = false - # Defined in extend/os: - # def formula; end - sig { returns(T::Boolean) } def HEAD? = false @@ -128,6 +125,8 @@ def context sig { returns(T.nilable(Symbol)) } def only_formula_or_cask + return unless respond_to?(:formula?) && respond_to?(:cask?) + if formula? && !cask? :formula elsif cask? && !formula? @@ -185,26 +184,18 @@ def cli_args @cli_args = [] @processed_options.each do |short, long| option = long || short - switch_val = invoke_if_respond_to(:"#{option_to_name(option)}?") - flag_val = invoke_if_respond_to(option_to_name(option).to_sym) - if switch_val == true || flag_val == true + switch = :"#{option_to_name(option)}?" + flag = option_to_name(option).to_sym + if @table[switch] == true || @table[flag] == true @cli_args << option - elsif flag_val.instance_of? String - @cli_args << "#{option}=#{flag_val}" - elsif flag_val.instance_of? Array - @cli_args << "#{option}=#{flag_val.join(",")}" + elsif @table[flag].instance_of? String + @cli_args << "#{option}=#{@table[flag]}" + elsif @table[flag].instance_of? Array + @cli_args << "#{option}=#{@table[flag].join(",")}" end end @cli_args.freeze end - - # FIXME: This is here for compatibility with the previous implementation that used OpenStruct. - # This is for args that need to determine whether they have been set or not, and thus may not support a default - # implementation. - sig { params(method: Symbol).returns(T.untyped) } - def invoke_if_respond_to(method) - public_send(method) if respond_to?(method) - end end end end diff --git a/Library/Homebrew/cli/parser.rb b/Library/Homebrew/cli/parser.rb index fc3860c4685bfb..a9363bba626408 100644 --- a/Library/Homebrew/cli/parser.rb +++ b/Library/Homebrew/cli/parser.rb @@ -252,7 +252,7 @@ def comma_array(name, description: nil, hidden: false) description = option_description(description, name, hidden:) process_option(name, description, type: :comma_array, hidden:) @parser.on(name, OptionParser::REQUIRED_ARGUMENT, Array, *wrap_option_desc(description)) do |list| - @args.define_singleton_method(option_to_name(name)) { list } + set_args_method(option_to_name(name).to_sym, list) end end @@ -277,7 +277,7 @@ def flag(*names, description: nil, replacement: nil, depends_on: nil, hidden: fa # This odisabled should stick around indefinitely. odisabled "the `#{names.first}` flag", replacement unless replacement.nil? names.each do |name| - @args.define_singleton_method(option_to_name(name)) { option_value } + set_args_method(option_to_name(name).to_sym, option_value) end end @@ -286,6 +286,17 @@ def flag(*names, description: nil, replacement: nil, depends_on: nil, hidden: fa end end + sig { params(name: Symbol, value: T.any(NilClass, T::Boolean, String, T::Array[String])).void } + def set_args_method(name, value) + @args.table[name] = value + return if @args.respond_to?(name) + + @args.define_singleton_method(name) do + T.bind(self, Args) + table.fetch(name) + end + end + sig { params(options: String).returns(T::Array[T::Array[String]]) } def conflicts(*options) @conflicts << options.map { |option| option_to_name(option) } @@ -552,7 +563,7 @@ def generate_banner def set_switch(*names, value:, from:) names.each do |name| @switch_sources[option_to_name(name)] = from - @args.define_singleton_method(:"#{option_to_name(name)}?") { value } + set_args_method(:"#{option_to_name(name)}?", value) end end @@ -564,7 +575,7 @@ def disable_switch(*args) else false end - @args.define_singleton_method(:"#{option_to_name(name)}?") { result } + set_args_method(:"#{option_to_name(name)}?", result) end end @@ -673,7 +684,7 @@ def process_option(*args, type:, hidden: false) disable_switch(*args) else args.each do |name| - @args.define_singleton_method(option_to_name(name)) { nil } + set_args_method(option_to_name(name).to_sym, nil) end end diff --git a/Library/Homebrew/extend/os/linux/cli/parser.rb b/Library/Homebrew/extend/os/linux/cli/parser.rb index ab9b0af05075c0..054332fc728fab 100644 --- a/Library/Homebrew/extend/os/linux/cli/parser.rb +++ b/Library/Homebrew/extend/os/linux/cli/parser.rb @@ -9,8 +9,14 @@ module Parser requires_ancestor { Homebrew::CLI::Parser } + sig { void } + def set_default_options + args.table[:"formula?"] = true if args.respond_to?(:formula?) + end + sig { void } def validate_options + return unless args.respond_to?(:cask?) return unless args.cask? # NOTE: We don't raise an error here because we don't want diff --git a/Library/Homebrew/extend/os/parser.rb b/Library/Homebrew/extend/os/parser.rb index 9c5095b4e227d8..fcf79902524d15 100644 --- a/Library/Homebrew/extend/os/parser.rb +++ b/Library/Homebrew/extend/os/parser.rb @@ -2,12 +2,3 @@ # frozen_string_literal: true require "extend/os/linux/cli/parser" if OS.linux? - -module Homebrew - module CLI - class Args - sig { returns(T::Boolean) } - def formula? = OS.linux? - end - end -end