require_relative '../dencli' class DenCli::CMD attr_reader :parent, :name, :description, :exe, :completion, :options def initialize parent, name, description, exe raise "Proc expected, instead of: #{exe.inspect}" unless Proc === exe @parent, @name, @description, @exe = parent, name, description, lambda( &exe) @options = [] completion {|*a| [] } end def _full_cmd( post) parent._full_cmd [@name]+post end def full_cmd() _full_cmd [] end def parameters() @exe.parameters end def required() @exe.parameters.select{|e|:req == e[0]}.map{|e|e[1]} end def additional() @exe.parameters.select{|e|:opt == e[0]}.map{|e|e[1]} end def call( *as) if @options.empty? @exe.call *as else os = {} options = OptionParser.new options.banner = "#{full_cmd.join ' '}" @options.each do |(aname, aas, aos, aexe)| os[aname] = aos[aname] if aos.has_key? :default options.on( *aas) {|val| os[aname] = aexe[val] } end as = options.parse! as if @exe.lambda? pars = required if as.length < pars.length raise DenCli::UsageError, "Missing parameter(s): #{pars[as.length..-1].join " "}" end if parameters.select{|e|:rest == e[0]}.empty? pars = pars + additional if as.length > pars.length raise DenCli::UsageError, "Unused parameter(s): #{as[-pars.length..-1].shelljoin}" end end end @exe.call *as, **os end end def usage "#{parent.full_cmd.join ' '} #{name} "+ @options.map{|(_,(o,_,_,_),_)|"[#{o}] "}.join( '')+ (@exe.lambda? ? ( required.join( " ")+ (additional.empty? ? "" : " [#{additional.join " "}]") ) : '...') end def help "#{usage}\n#{description}" end def complete( *pre, str) @completion.call *pre, str end def completion &exe @completion = exe self end def opt name, *as, **os, &exe @options.push [name.to_s.to_sym, as, os, exe || lambda{|val|val} ] self end def inspect "#<%s:0x%x %s @name=%p @description=%p @parent=<%s:0x%x %s> @exe=>" % [ self.class.name, self.object_id, self.full_cmd, @name, @description, @parent.class.name, @parent.class.object_id, @parent.full_cmd, @exe.arity ] end end