help_full for printing recursive all available commands. support for --[no-]opt.
This commit is contained in:
parent
d3930c00bc
commit
09c4c46d25
|
@ -5,15 +5,23 @@ $:.unshift Pathname.new(__FILE__).dirname.dirname.join('lib').to_s
|
||||||
require 'dencli'
|
require 'dencli'
|
||||||
|
|
||||||
cli = DenCli.new 'example', "This is an example for generate a DenCli-API"
|
cli = DenCli.new 'example', "This is an example for generate a DenCli-API"
|
||||||
cli.cmd( :example, "I have an example command") { STDERR.puts "This is an example" }
|
|
||||||
cli.cmd( :help, "", aliases: [nil, '-h', '--help']) {|*args| STDERR.puts cli.help(*args) }
|
|
||||||
|
|
||||||
cli.cmd( :args, "Expects and prints given arguments", &lambda {|a, b, c:, d:, e:|
|
cli.cmd( :args, "Expects and prints given arguments", &lambda {|a, b, c:, d:, e:|
|
||||||
p a: a, b: b, c: c, d: d, e: e
|
p a: a, b: b, c: c, d: d, e: e
|
||||||
}).
|
}).
|
||||||
opt( :c, '-c=ForC', "Option c").
|
opt( :c, '-c=ForC', "Option c").
|
||||||
opt( :d, '-d=ForD', "Option d", default: "something").
|
opt( :d, '-d=ForD', "Option d", default: "something").
|
||||||
opt( :e, '-e', "Toggle e", default: false)
|
opt( :e, '-e', "Toggle e", default: false).
|
||||||
|
opt( :f, '--[no-]f', "Toggle f", default: false)
|
||||||
|
|
||||||
|
cli.cmd( :example, "I have an example command") { STDERR.puts "This is an example" }
|
||||||
|
cli.cmd( :help, "", aliases: [nil, '-h', '--help'], &lambda {|*args, full:|
|
||||||
|
if full
|
||||||
|
cli.help_full *args, output: STDERR
|
||||||
|
else
|
||||||
|
cli.help *args, output: STDERR
|
||||||
|
end
|
||||||
|
}).
|
||||||
|
opt( :full, '-f', '--[no-]full', "Print all commands and sub-commands.", default: false)
|
||||||
|
|
||||||
cli.sub( :more, "Sub-Commands are also possible with a new cli") do |sub|
|
cli.sub( :more, "Sub-Commands are also possible with a new cli") do |sub|
|
||||||
sub.cmd( :help, "", aliases: [nil, '-h', '--help']) {|*args| STDERR.puts sub.help(*args) }
|
sub.cmd( :help, "", aliases: [nil, '-h', '--help']) {|*args| STDERR.puts sub.help(*args) }
|
||||||
|
@ -29,7 +37,7 @@ cli.sub( :more, "Sub-Commands are also possible with a new cli") do |sub|
|
||||||
opt( :e, '-e', "Toggle e")
|
opt( :e, '-e', "Toggle e")
|
||||||
|
|
||||||
sub.sub( :deeper, "You want to have Sub-Sub-Commands?") do |sub2|
|
sub.sub( :deeper, "You want to have Sub-Sub-Commands?") do |sub2|
|
||||||
sub2.cmd( :help, "", aliases: [nil, '-h', '--help'], &lambda {|*args| STDERR.puts sub2.help(*args) })
|
sub2.cmd( :help, "", aliases: [nil, '-h', '--help'], &lambda {|*args| sub2.help( *args, output: STDERR) })
|
||||||
sub2.cmd( :last, "The last example", &lambda { STDERR.puts "The last example" })
|
sub2.cmd( :last, "The last example", &lambda { STDERR.puts "The last example" })
|
||||||
|
|
||||||
sub2.sub( :'sub-commands', "Endless Sub-Sub- ...") do |sub3|
|
sub2.sub( :'sub-commands', "Endless Sub-Sub- ...") do |sub3|
|
||||||
|
|
|
@ -80,8 +80,22 @@ class DenCli
|
||||||
@subs.call *a
|
@subs.call *a
|
||||||
end
|
end
|
||||||
|
|
||||||
def help *args
|
def usage *args, **opts
|
||||||
@subs.help *args
|
@subs.usage *args, **opts
|
||||||
|
end
|
||||||
|
|
||||||
|
def help *args, **opts
|
||||||
|
@subs.help *args, **opts
|
||||||
|
end
|
||||||
|
|
||||||
|
def help_full *args, output: nil
|
||||||
|
output ||= STDOUT
|
||||||
|
x = @subs.goto *args
|
||||||
|
_help_full output, x
|
||||||
|
end
|
||||||
|
|
||||||
|
def _help_full output, subs
|
||||||
|
Sub._help_commands output, subs.to_enum( :commands)
|
||||||
end
|
end
|
||||||
|
|
||||||
def [] k
|
def [] k
|
||||||
|
|
|
@ -23,10 +23,11 @@ class DenCli::CMD
|
||||||
def options_required() @exe.parameters.select {|e| :keyreq == e[0] }.map {|e| e[1] } end
|
def options_required() @exe.parameters.select {|e| :keyreq == e[0] }.map {|e| e[1] } end
|
||||||
def options_additional() @exe.parameters.select {|e| :key == e[0] }.map {|e| e[1] } end
|
def options_additional() @exe.parameters.select {|e| :key == e[0] }.map {|e| e[1] } end
|
||||||
|
|
||||||
def help() "Usage: #{usage}\n#{description}\n#{options_help}" end
|
def complete *pre, str
|
||||||
def complete( *pre, str) @completion.call *pre, str end
|
@completion.call *pre, str
|
||||||
|
end
|
||||||
|
|
||||||
def call( *as)
|
def call *as
|
||||||
os = {}
|
os = {}
|
||||||
unless @options.empty?
|
unless @options.empty?
|
||||||
# options like --abc | -x will be provided in os
|
# options like --abc | -x will be provided in os
|
||||||
|
@ -53,25 +54,58 @@ class DenCli::CMD
|
||||||
end
|
end
|
||||||
kr = @options.select {|_, o| o.required? and not os.has_key? o.name }
|
kr = @options.select {|_, o| o.required? and not os.has_key? o.name }
|
||||||
unless kr.empty?
|
unless kr.empty?
|
||||||
raise DenCli::UsageError, "Missing argument(s): #{kr.map {|o| o.as.first }.join ', '}"
|
raise DenCli::UsageError, "Missing argument(s): #{kr.map {|o| o.long || o.short }.join ', '}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@exe.call *as, **os
|
@exe.call *as, **os
|
||||||
end
|
end
|
||||||
|
|
||||||
def usage
|
def usage output: nil
|
||||||
args =
|
output ||= ''
|
||||||
@options.map do |_, o|
|
_usage output
|
||||||
s = "#{o.short}#{o.val ? ?= : ''}#{o.val}"; o.required? ? "#{s} " : "[#{s}] "
|
output
|
||||||
end
|
|
||||||
"#{full_cmd.join ' '} #{args.join ''}"+
|
|
||||||
(@exe.lambda? ? (
|
|
||||||
required.map{|s|"<#{s}>"}.join( " ")+
|
|
||||||
(additional.empty? ? "" : " [#{additional.map{|s|"<#{s}>"}.join " "}]")
|
|
||||||
) : '...')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def options_help
|
def _usage output
|
||||||
|
output << full_cmd.join( ' ')
|
||||||
|
@options.each do |_, o|
|
||||||
|
s = "#{o.short||o.long}#{o.val ? ?= : ''}#{o.val}"
|
||||||
|
output << (o.required? ? " #{s}" : " [#{s}]")
|
||||||
|
end
|
||||||
|
if @exe.lambda?
|
||||||
|
required.each {|s| output << " <#{s}>" }
|
||||||
|
output << " [#{additional.map{|s|"<#{s}>"}.join " "}]" unless additional.empty?
|
||||||
|
else
|
||||||
|
output << ' ...'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def commands *args, &exe
|
||||||
|
yield self
|
||||||
|
end
|
||||||
|
|
||||||
|
def goto *args
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def help output: nil
|
||||||
|
output ||= ''
|
||||||
|
_help output
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def _help output
|
||||||
|
output << "Usage: #{usage}\n#{description}\n"
|
||||||
|
_help_options output
|
||||||
|
end
|
||||||
|
|
||||||
|
def help_options output: nil
|
||||||
|
output ||= ''
|
||||||
|
_help_options output
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def _help_options output
|
||||||
sc, lc, dc = 0, 0, 0
|
sc, lc, dc = 0, 0, 0
|
||||||
@options.each do |_, o|
|
@options.each do |_, o|
|
||||||
s = o.short&.length || 0
|
s = o.short&.length || 0
|
||||||
|
@ -89,8 +123,8 @@ class DenCli::CMD
|
||||||
end
|
end
|
||||||
dc = d if dc < d
|
dc = d if dc < d
|
||||||
end
|
end
|
||||||
format = " %-#{sc}s%s %-#{lc}s %s"
|
format = " %-#{sc}s%s %-#{lc}s %s\n"
|
||||||
@options.map {|_, o|
|
@options.map do |_, o|
|
||||||
s, l, v, y = o.short, o.long, o.val, ','
|
s, l, v, y = o.short, o.long, o.val, ','
|
||||||
if l.nil?
|
if l.nil?
|
||||||
s += "=#{v}" if v
|
s += "=#{v}" if v
|
||||||
|
@ -101,8 +135,8 @@ class DenCli::CMD
|
||||||
end
|
end
|
||||||
d = o.desc || ''
|
d = o.desc || ''
|
||||||
d += " (#{o.default})" if o.default?
|
d += " (#{o.default})" if o.default?
|
||||||
format % [ s, y, l, d ]
|
output << format % [ s, y, l, d ]
|
||||||
}.join "\n"
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def completion &exe
|
def completion &exe
|
||||||
|
@ -116,34 +150,29 @@ class DenCli::CMD
|
||||||
def default?() NilClass != @default end
|
def default?() NilClass != @default end
|
||||||
def default() NilClass == @default ? nil : @default end
|
def default() NilClass == @default ? nil : @default end
|
||||||
|
|
||||||
def initialize cmd, name, opt, *alts, desc, default: NilClass, **os, &conv
|
def parse_opt_string opt
|
||||||
long = short = val = nil
|
|
||||||
case opt
|
case opt
|
||||||
when /\A(--[^=-]+)=(.+)\z/
|
when /\A(--\[no-\][^=]+)\z/
|
||||||
long, val = $1, $2
|
@long, @val = $1, nil
|
||||||
when /\A(--[^=-]+)\z/
|
when /\A(--[^=]+)=(.+)\z/
|
||||||
long, val = $1, nil
|
@long, @val = $1, $2 || @val
|
||||||
|
when /\A(--[^=]+)\z/
|
||||||
|
@long, @val = $1, nil
|
||||||
when /\A(-[^=-]+)=(.+)\z/
|
when /\A(-[^=-]+)=(.+)\z/
|
||||||
short, val = $1, $2
|
@short, @val = $1, $2 || @val
|
||||||
when /\A(-[^=-]+)\z/
|
when /\A(-[^=-]+)\z/
|
||||||
short, val = $1, nil
|
@short, @val = $1, nil
|
||||||
else raise ArgumentError, "Unexpected format for option: #{opt.inspect}"
|
else
|
||||||
|
raise ArgumentError, "Unexpected format for option: #{opt.inspect}"
|
||||||
end
|
end
|
||||||
alts.each do |alt|
|
end
|
||||||
case alt
|
private :parse_opt_string
|
||||||
when /\A(--[^=-]+)=(.+)\z/
|
|
||||||
long, val = $1, val || $2
|
def initialize cmd, name, opt, *alts, desc, default: NilClass, **os, &conv
|
||||||
when /\A(--[^=-]+)\z/
|
@name, @desc, @default, @os, @conv, @val =
|
||||||
long, val = $1, nil
|
name.to_s.to_sym, desc, default, os, conv || lambda{|v|v}, nil
|
||||||
when /\A(-[^=-]+)=(.+)\z/
|
parse_opt_string opt
|
||||||
short, val = $1, val || $2
|
alts.each &method( :parse_opt_string)
|
||||||
when /\A(-[^=-]+)\z/
|
|
||||||
short, val = $1, nil
|
|
||||||
else raise ArgumentError, "Unexpected format for option: #{alt.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@name, @short, @long, @val, @desc, @default, @os, @conv =
|
|
||||||
name.to_s.to_sym, short, long, val, desc, default, os, conv || lambda{|v|v}
|
|
||||||
@req =
|
@req =
|
||||||
if NilClass != default
|
if NilClass != default
|
||||||
false
|
false
|
||||||
|
@ -155,6 +184,7 @@ class DenCli::CMD
|
||||||
end
|
end
|
||||||
|
|
||||||
def on parser, store
|
def on parser, store
|
||||||
|
store[@name] = @default if default?
|
||||||
parser.on "#{@short}#{@val ? ?= : ''}#{@val}", "#{@long}#{@val ? ?= : ''}#{@val}", **@os do |val|
|
parser.on "#{@short}#{@val ? ?= : ''}#{@val}", "#{@long}#{@val ? ?= : ''}#{@val}", **@os do |val|
|
||||||
store[@name] = @conv[val]
|
store[@name] = @conv[val]
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,16 +15,72 @@ class DenCli::Sub
|
||||||
"#{full_cmd.join ' '} ..."
|
"#{full_cmd.join ' '} ..."
|
||||||
end
|
end
|
||||||
|
|
||||||
def help n = nil, *a
|
def help n = nil, *a, output: nil
|
||||||
|
output ||= ''
|
||||||
|
_help output, n, *a
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def _help output, n = nil, *a
|
||||||
if n.nil?
|
if n.nil?
|
||||||
r = "#{full_cmd.join ' '}: #{description}\n\n"
|
output << "#{full_cmd.join ' '}: #{description}\n\n"
|
||||||
m = @subs.map {|k,c| k.nil? ? 0 : c.usage.length }.max
|
self.class._help_commands output, @subs
|
||||||
@subs.each do |k, c|
|
|
||||||
r += " % -#{m}s %s\n" % [c.usage, c.description] unless k.nil?
|
|
||||||
end
|
|
||||||
r
|
|
||||||
elsif @aliases.has_key? n
|
elsif @aliases.has_key? n
|
||||||
@aliases[n].help *a
|
@aliases[n]._help output, *a
|
||||||
|
else
|
||||||
|
raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def commands &exe
|
||||||
|
yield self
|
||||||
|
@subs.each {|k, c| c.commands &exe }
|
||||||
|
end
|
||||||
|
|
||||||
|
def help_commands output: nil
|
||||||
|
output ||= ''
|
||||||
|
self.class._help_commands output, subs.map {|_,c| c}
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def self._help_commands output, subs
|
||||||
|
m = subs.map {|c| x = c.usage.length; 25 < x ? 0 : x }.max
|
||||||
|
subs.each do |c|
|
||||||
|
if 25 < c.usage.length
|
||||||
|
output << "% -#{m}s\n#{' ' * m} " % [c.usage]
|
||||||
|
else
|
||||||
|
output << "% -#{m}s " % [c.usage]
|
||||||
|
end
|
||||||
|
n = m+2
|
||||||
|
prefix = nil
|
||||||
|
c.description.split /\n/ do |l|
|
||||||
|
c = 0
|
||||||
|
l.split %r< > do |w|
|
||||||
|
if prefix
|
||||||
|
output << prefix
|
||||||
|
prefix = nil
|
||||||
|
end
|
||||||
|
wl = w.length
|
||||||
|
if 75 < c+wl
|
||||||
|
output << "\n#{' ' * n}#{w}"
|
||||||
|
c = n+2+wl
|
||||||
|
else
|
||||||
|
output << " #{w}"
|
||||||
|
c += 1 + wl
|
||||||
|
end
|
||||||
|
end
|
||||||
|
prefix = "\n#{' ' * n}"
|
||||||
|
end
|
||||||
|
output << "\n"
|
||||||
|
end
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def goto *a
|
||||||
|
return self if a.empty?
|
||||||
|
n, *a = *a
|
||||||
|
if @aliases.has_key? n
|
||||||
|
@aliases[n].goto *a
|
||||||
else
|
else
|
||||||
raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
|
raise DenCli::UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.join ' '}"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue