# encoding: utf-8 module AssLauncher module Enterprise module Cli # 1C Enterprise cli parameters # Fuckin 1C have very obscure cli api # Parameters may have subparameters. All parameters and subparameters # expects argument or not. Some parameters may be modified with + or - or # other values like this: /Parm or /Param+ or /Param- or /Param:date:time. # # For implement binding parameters and subparameters, parameter have # +parent+ propery. If +parent+ property is +nil+ it is root parameter # and subpurameter if else module Parameters # Options for parameter DEFAULT_OPTIONS = { required: false, value_validator: nil, switch_list: nil, chose_list: nil, switch_value: nil }.freeze # Parameter name like it define in 1C cli api # Name may start with '/' or '-' key. Example: /Parameter or # -subpurameter # @return string attr_reader :name # Parameter help message # @return string attr_reader :desc # 1C binary type and version for which parameter defined # @return [BinaryMatcher] # @api private attr_reader :binary_matcher # Parameter group for build cli api help # @return [Symbol] attr_reader :group # 1C binary run mode for which parameter defined. see # {Cli::DEFINED_MODES} # @return [Array] attr_reader :modes # Parent parameter for subpurameter # @return kinde of [String] attr_reader :parent # Options see {DEFAULT_OPTIONS} # @return [Hash] attr_reader :options # Return +true+ if parameter defined for +binary_wrapper+ and +run_mode+ # @param binary_wrapper [BinaryWrapper::ThickClient # BinaryWrapper::ThinClient WebClient] # @param run_mode [Symbol] see {Parameters#modes} def match?(binary_wrapper, run_mode) binary_matcher.match?(binary_wrapper) && modes.include?(run_mode) end # Return +true+ if parameter defined for +version+ # @param version [Gem::Version] def match_version?(version) binary_matcher.requirement.satisfied_by? version end # :nodoc: def to_sym name.downcase.to_sym end # Returns parameter full name. Full name # composed from +full_name+ of all # {#parents} parameters # @return [String] def full_name return name if root? "#{parent.full_name}#{name}" end # Array of parent parameters # @return [Array] def parents return [] if root? parent.parents << parent end # Returns deep parameter in hierarchy tree # @return [Fixnum] def deep parents.size end # Returns +true+ if haven't {#parent} parameter def root? parent.nil? end # Returns +true+ if it's child of +expected_parent+ parameter # @param expected_parent kinde of [StringParam] def child?(expected_parent) return false if root? parent == expected_parent end # :nodoc: def to_s name.to_s end # Returns self with +value+ as 1C:Enterprise CLI argumets array # @example # param.name #=> "/Parm" # param.to_args 'value' #=> ["/Param", "value"] # @return [Array] def to_args(value) [key(value).to_s, value(value).to_s] end # Wrapper for {#options} def switch_list options[:switch_list] end # (see #switch_list) def chose_list options[:chose_list] end # (see #switch_list) def value_validator options[:value_validator] || proc { |value| value } end # (see #switch_list) def switch_value options[:switch_value] || proc { |value| value } end # (see #switch_list) def required? options[:required] end def key(_value) name end private :key def validate(value) value_validator.call value end private :validate def value(value) validate(value) value end private :value def def_options DEFAULT_OPTIONS end private :def_options # Builds usage message def usage fail NotImplementedError end def auto_binary_matcher(arg) return arg if arg.is_a? BinaryMatcher return BinaryMatcher.auto(modes, arg) if arg.is_a? String BinaryMatcher.auto modes end private :auto_binary_matcher # Array of subparameters # @return [Array] def childs @childs ||= [] end # Add subparameter into {#childs} array # @param param kinde of {StringParam} def add_child(param) childs << param end # Restricts parameter from +version+ # and do it recursively for all subparameters from {#childs} array # @param (see #match_version?) def restrict_from(version) restrict_childs(version) binary_matcher.requirement = restrict_v(binary_matcher.requirement, version) if match_version? version end def restrict_childs(version) childs.each do |p| p.restrict_from version end end private :restrict_childs def restrict_v(r, v) Gem::Version::Requirement.new r.to_s.split(','), "< #{v}" end private :restrict_v # Parameter expects string value class StringParam include Parameters # @api private # @param name [String] name of parameter like defined 1C cli api # @param desc [String] help description # @param binary_matcher [BinaryMatcher, String, nil] for which # parameter defined # - If nil will be build default matcher. # - If string. String value must be suitable for Gem::Requirement. # In this case, will be build matcher for version defined in # string and suitable bynary type detected on run_modes # @param group [Symbol] parameter group # @param modes [Array] run modes for which parameter defined # @param parent kinde of {StringParam} parent for subpurameter # @param options [Hash] see {Parameters::DEFAULT_OPTIONS} def initialize(name, desc, binary_matcher, group, modes, parent = nil, **options) @name = name @desc = desc @modes = modes || Cli::DEFINED_MODES @binary_matcher = auto_binary_matcher(binary_matcher) @group = group @options = def_options.merge options @parent = parent @parent.add_child(self) unless parent.nil? end # Parameter require argumet def argument_require arguments_count > 0 end # Count of parameter argumets def arguments_count 1 end end # Parameter expects filesystem path # Path string cam came from diferent sources # and have windows, unix or unix-cygwin format # It class instance make path string platform independet use # {AssLauncher::Support::Platforms::PathExtension} class class Path < StringParam # Extends for {Parameters::DEFAULT_OPTIONS}. # # Option: +:must_be+ expects +:exist+ or +:not_exist+ values. # # If +:exist+ given verified for path must be exists and # if +:not_exist+ given verified for pat must be not exists DEFAULT_OPTIONS = Parameters::DEFAULT_OPTIONS.merge(must_be: nil) include AssLauncher::Support::Platforms def default_options DEFAULT_OPTIONS end private :default_options # (see #switch_list) def must_be options[:must_be] end def value(value) validate(value) path = platform.path(value).realdirpath verify(path) path.to_s end private :value def verify(path) case must_be when :exist then must_exists(path) when :not_exist then must_not_exists(path) end end private :verify def must_exists(path) fail ArgumentError, "Wrong value for #{name}."\ " Path #{path} not exists" unless path.exist? end private :must_exists def must_not_exists(path) fail ArgumentError, "Wrong value for #{name}."\ " Path #{path} exists" if path.exist? end private :must_not_exists end # In 8.3.8 platform add CLI parameters like as # +/DumpExternalDataProcessorOrReportToFiles+ expects 2 argumetns class PathTwice < StringParam include AssLauncher::Support::Platforms def to_args(p1, p2) [key('').to_s, rdp_(p1).to_s, rdp_(p2).to_s] end def arguments_count 2 end def rdp_(p) platform.path(p).realdirpath end private :rdp_ end # Flag parameter not expects argument class Flag < StringParam # Returns self as 1C:Enterprise CLI argumets array like # +["/Flag", ""]+ # @param _ [nil] {Flag} not expects argument # @return (see StringParam#to_args) def to_args(_ = nil) super '' end # Count of parameter argumets def arguments_count 0 end end class SkippedError < StandardError; end # Stub for define stupid or not importand parameters # {#to_args} raises of {SkippedError} class Skip < StringParam def to_args(*values) fail SkippedError, "Parameter #{full_name} skipped and restricted for use" end def skip? true end end # Chose parameter expects argunment value from chose_list class Chose < StringParam def validate(value) fail ArgumentError, "Wrong value `#{value}' for #{name} parameter" unless\ valid?(value) end private :validate def valid?(value) chose_list.keys.map(&:to_s).map(&:downcase).include?\ value.to_s.downcase end end # Switch parameter is most stupid CLI parameter of 1C:Enterprise. # Switch parameter expects argument value from +:switch_list+ or # block +:switch_value+ which return modified value argument. # Switch parameter modifyed self name when uses parameter value # @example # # /UseHwLicenses have {:+=>'',:-=>''} switch_list and: # to_args(:+) # => ['/UseHwLicenses+',''] # to_args(:-) # => ['/UseHwLicenses-',''] # to_args(:bad_value) # => ArgumentError # # # -TimeLimit have block: # {switch_value: ->(value) { ":#{value}"}} # # and # to_args("12:00") # => ['-TimeLimit:12:00',''] class Switch < StringParam def value(_value) '' end private :value def key(value) "#{name}#{switch_value_get(value)}" end private :key def switch_value_get(value) if switch_list fail ArgumentError, "Wrong value #{value} for parameter #{name}" unless\ valid?(value) end validate(value) switch_value.call(value) end private :switch_value_get def valid?(value) switch_list.keys.map(&:to_s).map(&:downcase).include?\ value.to_s.downcase end end # List of parameters defined for one version # @raise (see #<<) class ParametersList # Array of parameters. # For add parameter into array use {#<<} def parameters @parameters ||= [] end # Returns +true+ if parameter +p+ presents in list # @param p kinde of {StringParam} def param_defined?(p) !find(p.name, p.parent).nil? end # Add parameter in to tail of list # @param p [StringParam Flag Path Switch Chose] parameter instance # @raise [ArgumentError] if parameter alrady present in list def <<(p) fail ArgumentError, "Parameter #{p.full_name} alrady defined" if\ param_defined?(p) parameters << p end alias_method :"+", :"<<" alias_method :add, :"<<" # Find parameter in list # @param name [String] name of finded parameter # @param parent [StringParam Flag Path Switch Chose] parent for # subparameter # @return [StringParam Flag Path Switch Chose nil] founded parameter def find(name, parent) parameters.each do |p| if p.to_sym == name.downcase.to_sym && p.parent == parent return p end end nil end # :nodoc: def each(&block) parameters.each(&block) end # (see Parameters#usage) def usage fail NotImplementedError end end # List of parameters defined for all versions # @raise (see #add) class AllParameters # Array of parameters. # For add parameter into array use {#add} def parameters @parameters ||= [] end # Add parameter into list # @param (see #param_defined?) # @raise [ArgumentError] if parameter +p.name+ alrady defined for # +version+ def add(p, version) fail_if_difined(p, version) parameters << p end def fail_if_difined(p, version) fail ArgumentError, "Parameter #{p.full_name} alrady defined for version"\ " #{version}" if param_defined? p, version end private :fail_if_difined # Returns +true+ if parameter +p+ for +version+ presents in list # @param p kinde of {StringParam} # @param version (see Parameters#match_version?) def param_defined?(p, version) find(p.name, p.parent).each do |p_| return true if p_.match_version? version end false end # Return parameters list for given +binary_wrapper+ and +run_mode+ # @return [ParametersList] for given +binary_wrapper+ and +run_mode+ # @param (see Parameters#match?) def to_parameters_list(binary_wrapper, run_mode) r = new_list parameters.each do |p| r << p if p.match? binary_wrapper, run_mode end r end def new_list ParametersList.new end private :new_list # Find actual parameter for +version+ # @return kinde of {StringParam} # @param name (see #find) # @param parent (see #find) # @param version [Gem::Version] def find_for_version(name, parent, version) r = nil find(name, parent).each do |p| r = oops!(r, p, version) if p.match_version? version end r end def oops!(r, p, v) fail "Too many params #{p.full_name} spec for #{v}" unless r.nil? p end private :oops! # Find all parameter versions # @param name (see StringParam#initialize) # @param parent (see StringParam#initialize) # @return [Array] of parameters def find(name, parent) parameters.select do |p| p.to_sym == name.downcase.to_sym && p.parent == parent end end end end end end end