# frozen_string_literal: true require 'immutable-struct' require_relative 'name' require_relative 'types' require_relative 'values' module RubyNPM module Options # rubocop:disable Metrics/ClassLength class Definition < ImmutableStruct.new( :name, :option_type, :value_type, :override_keys, :extra_keys, :separator, :placement, :repeatable? ) # rubocop:disable Metrics/MethodLength def initialize(opts) raise 'Missing name.' unless opts[:name] super( name: Name.new(opts[:name]), option_type: Types.resolve(opts[:option_type]) || Types::Standard, value_type: Values.resolve(opts[:value_type]) || Values::String, repeatable: opts[:repeatable] || false, separator: opts[:separator], placement: opts[:placement], extra_keys: { singular: [], plural: [] } .merge(opts[:extra_keys] || {}), override_keys: { singular: nil, plural: nil } .merge(opts[:override_keys] || {}) ) end # rubocop:enable Metrics/MethodLength def matches?(name) @name == Name.new(name) end def build(parameters) build_singular_options(parameters) + build_plural_options(parameters) end private def resolved_singular_key if override_keys[:singular] == false nil else override_keys[:singular] || name.as_singular_key end end def all_singular_keys ([resolved_singular_key] + extra_keys[:singular]).compact end def resolved_plural_key if override_keys[:plural] == false nil else override_keys[:plural] || name.as_plural_key end end def all_plural_keys ([resolved_plural_key] + extra_keys[:plural]).compact end def too_many_values?(values) !repeatable? && values.length > 1 end def values(parameters, keys) keys.map { |k| parameters[k] }.compact end def needs_plural?(value) repeatable? && !value.nil? end def only_singular?(value) !needs_plural?(value) end def key_valued?(value) value.respond_to?(:keys) end def multi_valued?(value) value.respond_to?(:each) end def build_option(value) option_type.new(name, value, separator: separator, placement: placement) end def build_value(value) value_type.new(value) end def build_key_value(key, value) Values::KeyValue.new(key, build_value(value)) end def build_singular(value) build_single_option(value) end def build_singulars(values) values.map { |p| build_singular(p) }.flatten end def build_singular_options(parameters) keys = all_singular_keys values = values(parameters, keys) if too_many_values?(values) raise "Multiple values provided for '#{name}' " \ "(with keys #{keys}) and option not repeatable." end build_singulars(values) end def build_plural(value) if only_singular?(value) build_no_options elsif key_valued?(value) build_key_value_options(value) elsif multi_valued?(value) build_multiple_options(value) else build_single_option(value) end end def build_plurals(values) values.map { |p| build_plural(p) }.flatten end def build_plural_options(parameters) keys = all_plural_keys values = values(parameters, keys) build_plurals(values) end def build_key_value_options(value) value.map { |k, v| build_option(build_key_value(k, v)) } end def build_multiple_options(value) value.map { |v| build_option(build_value(v)) } end def build_single_option(value) [build_option(build_value(value))] end def build_no_options [] end end # rubocop:enable Metrics/ClassLength end end