class Git::Commands::OperandAllocator
Allocates operand (positional argument) values to definitions following Ruby semantics.
This class handles the complex logic of mapping positional values to their definitions, supporting required, optional, and repeatable operands.
@api private
Public Class Methods
Source
# File lib/git/commands/arguments.rb, line 4161 def initialize(definitions) @definitions = definitions end
@param definitions [Array<Hash>] operand definitions
Public Instance Methods
Source
# File lib/git/commands/arguments.rb, line 4170 def allocate(values) allocation = {} repeatable_index = @definitions.find_index { |d| d[:repeatable] } consumed = if repeatable_index.nil? allocate_without_repeatable(values, allocation) else allocate_with_repeatable(values, allocation, repeatable_index) end [allocation, consumed] end
Allocate values to definitions
@param values [Array] the positional argument values
@return [Array(Hash, Integer)] tuple of allocation hash and consumed count
Private Instance Methods
Source
# File lib/git/commands/arguments.rb, line 4248 def allocate_leading(allocation, definitions, values) return 0 if definitions.empty? state = LeadingAllocationState.new(definitions, values, method(:required?)) state.allocate(allocation) end
Allocate leading positionals (those before any trailing required) Required positionals consume values; optional ones only consume if extras available
@param allocation [Hash] the allocation hash to populate
@param definitions [Array<Hash>] the leading operand definitions
@param values [Array] the values available for leading allocation
@return [Integer] the number of non-nil positionals consumed
@api private
Source
# File lib/git/commands/arguments.rb, line 4406 def allocate_post_repeatable(allocation, definitions, values, post_start) consumed = 0 definitions.each_with_index do |definition, offset| pos_index = post_start + offset value = pos_index < values.size ? values[pos_index] : nil allocation[definition[:name]] = value.nil? ? definition[:default] : value consumed += 1 if pos_index < values.size && !values[pos_index].nil? end consumed end
Allocate post-repeatable positionals from the end of the values array
@param allocation [Hash] the allocation hash to populate
@param definitions [Array<Hash>] the post-repeatable operand definitions
@param values [Array] the full values array
@param post_start [Integer] the index where post definitions begin
@return [Integer] the number of non-nil positionals consumed
@api private
Source
# File lib/git/commands/arguments.rb, line 4360 def allocate_pre_repeatable_smart(allocation, definitions, values) return 0 if definitions.empty? state = LeadingAllocationState.new(definitions, values, method(:required?)) state.allocate(allocation) end
Allocate pre-repeatable positionals with Ruby-like semantics (required get values first, optional only if extra values available)
@param allocation [Hash] the allocation hash to populate
@param definitions [Array<Hash>] the pre-repeatable operand definitions
@param values [Array] the values available for pre-repeatable allocation
@return [Integer] the number of non-nil positionals consumed
@api private
Source
# File lib/git/commands/arguments.rb, line 4382 def allocate_repeatable(allocation, definition, values, start_idx, end_idx) repeatable_values = values[start_idx...end_idx] || [] allocation[definition[:name]] = if repeatable_values.empty? || repeatable_values.all?(&:nil?) definition[:default] else repeatable_values end repeatable_values.compact.size end
Allocate the repeatable definition’s slice of values
@param allocation [Hash] the allocation hash to populate
@param definition [Hash] the repeatable operand definition
@param values [Array] the full values array
@param start_idx [Integer] the start index of the repeatable slice
@param end_idx [Integer] the end index (exclusive) of the repeatable slice
@return [Integer] the number of non-nil values consumed by the repeatable
@api private
Source
# File lib/git/commands/arguments.rb, line 4266 def allocate_trailing(allocation, definitions, values) consumed = 0 definitions.each_with_index do |definition, index| allocation[definition[:name]] = index < values.size ? values[index] : definition[:default] consumed += 1 if index < values.size end consumed end
Allocate trailing required positionals from the end of the values array
@param allocation [Hash] the allocation hash to populate
@param definitions [Array<Hash>] the trailing operand definitions
@param values [Array] the values remaining for trailing allocation
@return [Integer] the number of non-nil positionals consumed
@api private
Source
# File lib/git/commands/arguments.rb, line 4286 def allocate_with_repeatable(values, allocation, repeatable_index) parts = split_around_repeatable(repeatable_index) slices = calculate_repeatable_slices(values, parts) pre_consumed = allocate_pre_repeatable_smart(allocation, parts[:pre], slices[:pre_values]) repeatable_consumed = allocate_repeatable( allocation, parts[:repeatable], values, slices[:var_start], slices[:var_end] ) post_consumed = allocate_post_repeatable(allocation, parts[:post], values, slices[:post_start]) pre_consumed + repeatable_consumed + post_consumed end
Allocate values when a repeatable definition is present
@param values [Array] the positional argument values
@param allocation [Hash] the allocation hash to populate
@param repeatable_index [Integer] the index of the repeatable definition
@return [Integer] total non-nil positionals consumed
@api private
Source
# File lib/git/commands/arguments.rb, line 4197 def allocate_without_repeatable(values, allocation) trailing = count_trailing_required leading_defs = @definitions[0...(@definitions.size - trailing)] trailing_defs = @definitions[(@definitions.size - trailing)..] values_for_leading = [values.size - trailing, 0].max leading_values = values[0...values_for_leading] trailing_values = values[values_for_leading..] consumed = allocate_leading(allocation, leading_defs, leading_values) consumed + allocate_trailing(allocation, trailing_defs, trailing_values) end
Allocate when there’s no repeatable positional, following Ruby semantics:
-
Required positionals at the END are reserved first
-
Leading positionals get remaining values left-to-right
-
Optional positionals are skipped when there aren’t enough values
@param values [Array] the positional argument values
@param allocation [Hash] the allocation hash to populate
@return [Integer] the number of non-nil positionals consumed
@api private
Source
# File lib/git/commands/arguments.rb, line 4308 def calculate_repeatable_slices(values, parts) post_required_count = count_required(parts[:post]) pre_available = [values.size - post_required_count, 0].max pre_end = [pre_available, parts[:pre].size].min post_start = [values.size - parts[:post].size, pre_end].max { pre_values: values[0...pre_end], var_start: pre_end, var_end: post_start, post_start: post_start } end
Calculate start/end indices for pre, repeatable, and post slices
@param values [Array] the full positional values array
@param parts [Hash] the pre/repeatable/post definition parts
@return [Hash] slice indices: :pre_values, :var_start, :var_end, :post_start
@api private
Source
# File lib/git/commands/arguments.rb, line 4329 def count_required(definitions) definitions.count { |d| required?(d) } end
Count required definitions in the given list
@param definitions [Array<Hash>] the operand definitions to count
@return [Integer] the number of required definitions
@api private
Source
# File lib/git/commands/arguments.rb, line 4215 def count_trailing_required count = 0 @definitions.reverse_each do |d| break unless required?(d) count += 1 end count end
Count the number of trailing required definitions
@return [Integer] number of required definitions at the end of @definitions
@api private
Source
# File lib/git/commands/arguments.rb, line 4232 def required?(definition) definition[:required] && definition[:default].nil? end
Return true if a definition is required (required and has no default)
@param definition [Hash] the operand definition hash
@return [Boolean] true if the operand must be provided and has no default
@api private
Source
# File lib/git/commands/arguments.rb, line 4340 def split_around_repeatable(repeatable_index) { pre: @definitions[0...repeatable_index], repeatable: @definitions[repeatable_index], post: @definitions[(repeatable_index + 1)..] } end
Split @definitions into pre, repeatable, and post parts
@param repeatable_index [Integer] the index of the repeatable definition
@return [Hash] parts hash with :pre, :repeatable, and :post keys
@api private