2020-12-27 23:00:53 +01:00
require_relative '../dencli'
class DenCli :: Sub
2022-01-30 01:41:54 +01:00
attr_reader :parent , :name , :description , :subs , :aliases , :defined_in
2020-12-27 23:00:53 +01:00
2022-01-30 01:41:54 +01:00
def initialize parent , name , description , noshortaliases : nil , defined_in : nil
2021-12-07 23:48:28 +01:00
#DenCli::assert_type self, __method__, :name, name, Symbol
#DenCli::assert_type self, __method__, :parent, parent, DenCli, DenCli::Sub
#DenCli::assert_type self, __method__, :description, description, String
2021-01-03 13:42:00 +01:00
@parent , @name , @description , @subs , @aliases = parent , name , " -> #{ description } " , { } , { }
2022-01-30 01:41:54 +01:00
@noshortaliases , @defined_in = ! ! noshortaliases , defined_in || Kernel . caller
2020-12-27 23:00:53 +01:00
end
def _full_cmd ( post ) parent . _full_cmd [ @name ] + post end
def full_cmd ( ) _full_cmd [ ] end
2022-01-30 14:18:20 +01:00
def [] ( name ) @aliases [ name & . to_s ] end
def has? ( name ) @aliases . has_key? name & . to_s end
2020-12-27 23:00:53 +01:00
2021-12-08 23:52:05 +01:00
def usage output : nil
output || = ''
_usage output
output
end
def _usage output
output << full_cmd . join ( ' ' )
if @aliases . has_key? nil
output << " [<command> ...] "
else
output << " <command> [...] "
end
2021-01-03 16:28:12 +01:00
end
2021-11-30 23:09:06 +01:00
def help n = nil , * a , output : nil
output || = ''
_help output , n , * a
output
end
def _help output , n = nil , * a
2020-12-27 23:00:53 +01:00
if n . nil?
2021-11-30 23:09:06 +01:00
output << " #{ full_cmd . join ' ' } : #{ description } \n \n "
self . class . _help_commands output , @subs
2022-01-30 14:18:20 +01:00
elsif has? n
self [ n ] . _help output , * a
2021-11-30 23:09:06 +01:00
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
2021-12-07 23:48:28 +01:00
yield name , self
2021-11-30 23:09:06 +01:00
@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
2021-12-08 23:52:05 +01:00
class << self
def _help_commands output , subs
m = subs . map { | _name , c | x = c . usage . length ; 25 < x ? 0 : x } . max
subs . each do | _name , 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 / ) . each do | l |
c = 0
l . split ( %r< > ) . each 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
2021-11-30 23:09:06 +01:00
end
2021-12-08 23:52:05 +01:00
prefix = " \n #{ ' ' * n } "
2021-11-30 23:09:06 +01:00
end
2021-12-08 23:52:05 +01:00
output << " \n "
2021-11-30 23:09:06 +01:00
end
2021-12-08 23:52:05 +01:00
output
2021-11-30 23:09:06 +01:00
end
end
def goto * a
return self if a . empty?
n , * a = * a
2022-01-30 14:18:20 +01:00
if has? n
self [ n ] . goto * a
2020-12-27 23:00:53 +01:00
else
2020-12-28 00:29:54 +01:00
raise DenCli :: UnknownCommand , " unknown command: #{ _full_cmd ( [ n ] ) [ 1 .. - 1 ] . join ' ' } , available for #{ full_cmd [ 1 .. - 1 ] . join ' ' } : #{ @subs . keys . join ' ' } "
2020-12-27 23:00:53 +01:00
end
end
def call * a
n , * a = * a
2022-01-30 14:18:20 +01:00
if has? n
self [ n ] . call * a
2020-12-27 23:00:53 +01:00
else
2020-12-28 00:29:54 +01:00
raise DenCli :: UnknownCommand , " unknown command: #{ _full_cmd ( [ n ] ) [ 1 .. - 1 ] . join ' ' } , available for #{ full_cmd [ 1 .. - 1 ] . join ' ' } : #{ @subs . keys . join ' ' } "
2020-12-27 23:00:53 +01:00
end
end
def _add name , min , obj , aliases
2022-01-30 14:18:20 +01:00
#DenCli::assert_type self, __method__, :name, name, Symbol, String
2021-12-07 23:48:28 +01:00
#DenCli::assert_type self, __method__, :min, min, Integer, NilClass
#DenCli::assert_type self, __method__, :obj, obj, DenCli::Sub, DenCli::CMD
#DenCli::assert_type self, __method__, :aliases, aliases, Array, NilClass
2022-01-30 14:18:20 +01:00
name = name . to_s
2020-12-27 23:00:53 +01:00
@subs [ name ] = obj
2021-12-09 13:17:13 +01:00
if @noshortaliases
2022-01-30 01:41:54 +01:00
warn " Command/Alias for #{ obj . full_cmd } defined in #{ obj . defined_in } already exists: #{ full_cmd . join ' ' } #{ name } . Used by #{ @aliases [ name ] . full_cmd } defined in #{ @aliases [ name ] . defined_in } " if @aliases . has_key? name
2021-12-09 13:17:13 +01:00
@aliases [ name ] = obj
2021-12-09 14:04:27 +01:00
else
DenCli . gen_aliases name , min do | a |
2022-01-30 01:41:54 +01:00
warn " Command/Alias for #{ obj . full_cmd } defined in #{ obj . defined_in } already exists: #{ full_cmd . join ' ' } #{ a } . Used by #{ @aliases [ a ] . full_cmd } defined in #{ @aliases [ a ] . defined_in } " if @aliases . has_key? a
2021-12-09 14:04:27 +01:00
@aliases [ a ] || = obj
end
2021-12-09 13:17:13 +01:00
end
2020-12-27 23:00:53 +01:00
if aliases
2021-12-09 13:17:13 +01:00
[ * aliases ] . each do | a |
2022-01-30 14:18:20 +01:00
a = a & . to_s
2022-01-30 01:41:54 +01:00
raise ArgumentError , " Alias for #{ obj . full_cmd } defined in #{ obj . defined_in } already exists: #{ full_cmd . join ' ' } #{ a } . Used by #{ @aliases [ a ] . full_cmd } defined in #{ @aliases [ a ] . defined_in } " if @aliases . has_key? a
2021-12-09 13:17:13 +01:00
@aliases [ a ] = obj
end
2020-12-27 23:00:53 +01:00
end
obj
end
private :_add
2022-01-30 14:18:20 +01:00
# Define a new sub-menu:
#
# DenCli.new {|c|
# c.sub( 'sub-command') {|s|
# s.cmd( :hello, 'Greetings', &lambda {|| puts 'hello world' })
# }
# }
#
# # ./prog sub-command hello
#
# name should be a string/symbol. It will be converted to string.
# If provided, aliases must be a list of different aliases. It will be converted to string.
2022-01-30 01:41:54 +01:00
def sub name , description , min : nil , aliases : nil , noshortaliases : nil , defined_in : nil , & exe
2022-01-30 14:18:20 +01:00
r = _add name . to_s , min , DenCli :: Sub . new ( self , name , description , noshortaliases : noshortaliases , defined_in : defined_in || Kernel . caller . first ) , aliases
2020-12-27 23:00:53 +01:00
block_given? ? yield ( r ) : r
end
2022-01-30 14:18:20 +01:00
# Define a new command:
#
# DenCli.new {|c|
# c.cmd( :hello, 'Greetings', &lambda {|| puts 'hello world' })
# }
#
# # ./prog hello
# hello world
#
# name should be a string/symbol. It will be converted to string.
# If provided, aliases must be a list of different aliases. Except of nil, any alias will be converted to string.
# nil is an alias for a default command for sub-commands, but interactive shells.
#
# DenCli.new {|c|
# c.sub( :greetings, 'Hello, Welcome, ...') do |s|
# s.cmd( :hello, 'A simple Hello', aliases: %w[hello-world hello_world], &lambda {|| puts 'Hello World' })
# s.cmd( :welcome, 'More gracefull', aliases: [nil, 'welcome-world', :hello_world], &lambda {|| puts 'Welcome World' })
# }
# }
#
# # ./prog greetings
# Welcome World
# # ./prog greetings welcome
# Welcome World
# # ./prog greetings hello
# Hello World
2022-01-30 01:41:54 +01:00
def cmd name , description , min : nil , aliases : nil , defined_in : nil , & exe
_add name , min , DenCli :: CMD . new ( self , name , description , exe , defined_in || Kernel . caller . first ) , aliases
2020-12-27 23:00:53 +01:00
end
def complete * pre , str
if pre . empty?
@subs . keys . grep / \ A #{ Regexp . escape str } /
elsif sub = @subs [ pre [ 0 ] ]
sub . complete * pre [ 1 .. - 1 ] , str
else
2021-12-09 13:02:12 +01:00
$stdout . print " \a "
2020-12-27 23:00:53 +01:00
end
end
def inspect
2021-01-03 13:42:00 +01:00
" # <%s:0x%x %s @name=%p @description=%p @subs={%s} @aliases={%s} @parent=<%s:0x%x %s>> " % [
2020-12-27 23:00:53 +01:00
self . class . name , self . object_id , self . full_cmd ,
2021-01-03 13:42:00 +01:00
@name , @description , @subs . keys . join ( ', ' ) , @aliases . keys . join ( ', ' ) , @parent . class . name , @parent . class . object_id , @parent . full_cmd
2020-12-27 23:00:53 +01:00
]
end
end