210 lines
6.3 KiB
Ruby
Raw Normal View History

2024-08-18 20:30:58 -07:00
# typed: strict
# frozen_string_literal: true
require "ostruct"
module Homebrew
module CLI
class Args < OpenStruct
2024-08-18 20:30:58 -07:00
# FIXME: Enable cop again when https://github.com/sorbet/sorbet/issues/3532 is fixed.
# rubocop:disable Style/MutableConstant
2024-08-19 09:47:16 -07:00
# Represents a processed option. The array elements are:
# 0: short option name (e.g. "-d")
# 1: long option name (e.g. "--debug")
# 2: ???
# 3: option description (e.g. "Print debugging information")
# 4: whether the option is hidden)
OptionsType = T.type_alias { T::Array[[String, T.nilable(String), NilClass, String, T::Boolean]] }
2024-08-18 20:30:58 -07:00
# rubocop:enable Style/MutableConstant
sig { returns(T::Array[String]) }
attr_reader :options_only, :flags_only
# undefine tap to allow --tap argument
undef tap
2020-10-20 12:03:48 +02:00
sig { void }
def initialize
2020-11-29 21:23:44 +01:00
require "cli/named_args"
2024-05-23 17:08:41 +01:00
super
2024-08-18 20:30:58 -07:00
@cli_args = T.let(nil, T.nilable(T::Array[String]))
@processed_options = T.let([], OptionsType)
@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])
# Can set these because they will be overwritten by freeze_named_args!
# (whereas other values below will only be overwritten if passed).
self[:named] = NamedArgs.new(parent: self)
self[:remaining] = []
end
2024-08-18 20:30:58 -07:00
sig { params(remaining_args: T::Array[T.any(T::Array[String], String)]).void }
def freeze_remaining_args!(remaining_args)
self[:remaining] = remaining_args.freeze
end
2024-08-18 20:30:58 -07:00
sig { params(named_args: T::Array[String], cask_options: T::Boolean, without_api: T::Boolean).void }
def freeze_named_args!(named_args, cask_options:, without_api:)
options = {}
options[:force_bottle] = true if self[:force_bottle?]
options[:override_spec] = :head if self[:HEAD?]
options[:flags] = flags_only unless flags_only.empty?
self[:named] = NamedArgs.new(
*named_args.freeze,
parent: self,
2024-03-07 16:20:20 +00:00
cask_options:,
without_api:,
**options,
)
end
2024-08-18 20:30:58 -07:00
sig { params(processed_options: OptionsType).void }
def freeze_processed_options!(processed_options)
# Reset cache values reliant on processed_options
@cli_args = nil
@processed_options += processed_options
@processed_options.freeze
2024-08-18 20:30:58 -07:00
@options_only = cli_args.select { _1.start_with?("-") }.freeze
@flags_only = cli_args.select { _1.start_with?("--") }.freeze
end
2019-09-25 14:21:06 +05:30
sig { returns(NamedArgs) }
2019-09-08 19:56:24 +05:30
def named
2020-11-30 04:18:23 +01:00
require "formula"
self[:named]
2019-09-08 19:56:24 +05:30
end
2024-08-18 20:30:58 -07:00
sig { returns(T::Boolean) }
def no_named? = named.blank?
2020-03-04 17:23:20 +00:00
2024-08-18 20:30:58 -07:00
sig { returns(T::Array[String]) }
def build_from_source_formulae
2021-03-18 14:46:48 +00:00
if build_from_source? || self[:HEAD?] || self[:build_bottle?]
2021-07-27 05:12:15 +01:00
named.to_formulae.map(&:full_name)
else
[]
end
end
2024-08-18 20:30:58 -07:00
sig { returns(T::Array[String]) }
def include_test_formulae
if include_test?
named.to_formulae.map(&:full_name)
else
[]
end
end
2024-08-18 20:30:58 -07:00
sig { params(name: String).returns(T.nilable(String)) }
def value(name)
arg_prefix = "--#{name}="
flag_with_value = flags_only.find { |arg| arg.start_with?(arg_prefix) }
return unless flag_with_value
flag_with_value.delete_prefix(arg_prefix)
end
2020-10-20 12:03:48 +02:00
sig { returns(Context::ContextStruct) }
def context
Context::ContextStruct.new(debug: debug?, quiet: quiet?, verbose: verbose?)
end
2024-08-18 20:30:58 -07:00
sig { returns(T.nilable(Symbol)) }
2020-12-16 20:46:47 +09:00
def only_formula_or_cask
if formula? && !cask?
:formula
elsif cask? && !formula?
:cask
end
2020-12-15 23:43:46 +09:00
end
sig { returns(T::Array[[Symbol, Symbol]]) }
def os_arch_combinations
skip_invalid_combinations = false
oses = case (os_sym = os&.to_sym)
when nil
[SimulateSystem.current_os]
when :all
skip_invalid_combinations = true
OnSystem::ALL_OS_OPTIONS
else
[os_sym]
end
arches = case (arch_sym = arch&.to_sym)
when nil
[SimulateSystem.current_arch]
when :all
skip_invalid_combinations = true
OnSystem::ARCH_OPTIONS
else
[arch_sym]
end
oses.product(arches).select do |os, arch|
if skip_invalid_combinations
2024-03-07 16:20:20 +00:00
bottle_tag = Utils::Bottles::Tag.new(system: os, arch:)
bottle_tag.valid_combination?
else
true
end
end
end
private
2024-08-18 20:30:58 -07:00
sig { params(option: String).returns(String) }
def option_to_name(option)
option.sub(/\A--?/, "")
.tr("-", "_")
end
2024-08-18 20:30:58 -07:00
sig { returns(T::Array[String]) }
def cli_args
return @cli_args if @cli_args
2020-04-05 17:39:30 +05:30
@cli_args = []
@processed_options.each do |short, long|
option = long || short
switch = :"#{option_to_name(option)}?"
flag = option_to_name(option).to_sym
if @table[switch] == true || @table[flag] == true
@cli_args << option
elsif @table[flag].instance_of? String
2020-08-19 17:12:32 +01:00
@cli_args << "#{option}=#{@table[flag]}"
elsif @table[flag].instance_of? Array
2020-08-19 17:12:32 +01:00
@cli_args << "#{option}=#{@table[flag].join(",")}"
end
end
@cli_args.freeze
2020-04-05 17:39:30 +05:30
end
2024-08-18 20:30:58 -07:00
sig { params(method_name: Symbol, _include_private: T::Boolean).returns(T::Boolean) }
def respond_to_missing?(method_name, _include_private = false)
@table.key?(method_name)
2021-03-18 14:46:48 +00:00
end
2024-08-18 20:30:58 -07:00
sig { params(method_name: Symbol, args: T.untyped).returns(T.untyped) }
2021-03-18 14:46:48 +00:00
def method_missing(method_name, *args)
return_value = super
# Once we are frozen, verify any arg method calls are already defined in the table.
# The default OpenStruct behaviour is to return nil for anything unknown.
if frozen? && args.empty? && !@table.key?(method_name)
raise NoMethodError, "CLI arg for `#{method_name}` is not declared for this command"
end
return_value
end
end
end
end