184 lines
4 KiB
Ruby
184 lines
4 KiB
Ruby
|
class DenCli
|
||
|
class UsageError < ::RuntimeError
|
||
|
end
|
||
|
class UnknownCommand < UsageError
|
||
|
end
|
||
|
|
||
|
class <<self
|
||
|
# Helper Function for generate Regular Expressions of string,
|
||
|
# which matches all strings which has parts fron beginning of the given string.
|
||
|
# `n("abc")` would produce: `/(?:a|ab|abc)/`
|
||
|
# You can define a minimum length to match:
|
||
|
# `n("abcdef",4)` => `/abcd(?:|e|ef)/`
|
||
|
def n s, min = nil
|
||
|
min ||= 1
|
||
|
/#{s.length <= min ?
|
||
|
Regexp.quote(s) :
|
||
|
"#{Regexp.quote s[0...min]}#{
|
||
|
s[min...-1].
|
||
|
reverse.
|
||
|
each_char.
|
||
|
reduce( "#{Regexp.quote s[-1]}?") {|f,n|
|
||
|
"(?:#{Regexp.quote n}#{f})?"
|
||
|
}
|
||
|
}"
|
||
|
}/
|
||
|
end
|
||
|
|
||
|
# Wraps `n(s,min=)` in a full matching RegExp with ending `\0`:
|
||
|
# `r("abc")` would produce: `/\A(?:a|ab|abc)\0\z/`
|
||
|
# You can define a minimum length to match:
|
||
|
# `r("abcdef",4)` => `/\aabcd(?:|e|ef)\0\z/`
|
||
|
def r s, min = nil
|
||
|
/\A#{n s, min}\0\z/
|
||
|
end
|
||
|
|
||
|
# Generates a list of aliases for given `cmd`:
|
||
|
# `g(:abc)` => `["a", "ab", "abc"]`
|
||
|
# `g(:abcdef, 4)` => `["abcd", "abcde", "abcdef"]`
|
||
|
def gen_aliases cmd, min = nil
|
||
|
r = ((min||1)-1).upto cmd.length-1
|
||
|
if block_given?
|
||
|
r.each {|i| yield cmd[0..i] }
|
||
|
else
|
||
|
r.map {|i| cmd[0..i] }
|
||
|
end
|
||
|
end
|
||
|
alias g gen_aliases
|
||
|
end
|
||
|
|
||
|
class CMD
|
||
|
attr_reader :parent, :name, :desc, :exe
|
||
|
def initialize parent, name, desc, exe
|
||
|
raise "Proc expected, instead of: #{exe.inspect}" unless Proc === exe
|
||
|
@parent, @name, @desc, @exe = parent, name, desc, exe
|
||
|
end
|
||
|
|
||
|
def _full_cmd post
|
||
|
parent._full_cmd [@name]+post
|
||
|
end
|
||
|
|
||
|
def full_cmd
|
||
|
_full_cmd []
|
||
|
end
|
||
|
|
||
|
def call *a
|
||
|
@exe.call *a
|
||
|
end
|
||
|
|
||
|
def help
|
||
|
"#{parent.full_cmd.join ' '} #{name}\n#{ desc}"
|
||
|
end
|
||
|
|
||
|
def inspect
|
||
|
"#<%s:0x%x %s @name=%p @desc=%p @parent=<%s:0x%x %s> @exe=<arity=%d>>" % [
|
||
|
self.class.name, self.object_id, self.full_cmd,
|
||
|
@name, @desc, @parent.class.name, @parent.class.object_id, @parent.full_cmd,
|
||
|
@exe.arity
|
||
|
]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
class Sub
|
||
|
attr_reader :parent, :name, :desc, :subs
|
||
|
def initialize parent, name, desc
|
||
|
@parent, @name, @desc, @subs, @aliases = parent, name, desc, {}, {}
|
||
|
end
|
||
|
|
||
|
def _full_cmd post
|
||
|
parent._full_cmd [@name]+post
|
||
|
end
|
||
|
|
||
|
def full_cmd
|
||
|
_full_cmd []
|
||
|
end
|
||
|
|
||
|
def [] k
|
||
|
@aliases[k]
|
||
|
end
|
||
|
|
||
|
def help n = nil, *a
|
||
|
if n.nil?
|
||
|
r = "#{full_cmd.join ' '}: #{desc}\n\n"
|
||
|
m = @subs.map {|k,_| k.length }.max
|
||
|
@subs.each do |k, c|
|
||
|
r += " % -#{m}s %s\n" % [k, c.desc] unless k.nil?
|
||
|
end
|
||
|
r
|
||
|
elsif @aliases.has_key? n
|
||
|
@aliases[n].help *a
|
||
|
else
|
||
|
raise UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.inspect}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def call *a
|
||
|
n, *a = *a
|
||
|
if @aliases.has_key? n
|
||
|
@aliases[n].call *a
|
||
|
else
|
||
|
raise UnknownCommand, "unknown command: #{_full_cmd( [n])[1..-1].join ' '}, available for #{full_cmd[1..-1].join' '}: #{@subs.keys.inspect}"
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def _add name, min, obj, aliases
|
||
|
name = name.to_s unless name.nil?
|
||
|
@subs[name] = obj
|
||
|
CL.gen_aliases( name, min) {|a| @aliases[a] = obj }
|
||
|
if aliases
|
||
|
[*aliases].each {|a| @aliases[a] = obj }
|
||
|
end
|
||
|
obj
|
||
|
end
|
||
|
private :_add
|
||
|
|
||
|
def sub name, desc, min: nil, aliases: nil, &exe
|
||
|
r = _add name, min, Sub.new( self, name, desc), aliases
|
||
|
block_given? ? yield( r) : r
|
||
|
end
|
||
|
|
||
|
def cmd name, desc, min: nil, aliases: nil, &exe
|
||
|
_add name, min, CMD.new( self, name, desc, exe), aliases
|
||
|
end
|
||
|
|
||
|
def inspect
|
||
|
"#<%s:0x%x %s @name=%p @desc=%p @subs={%s} @aliases={%s} @parent=<%s:0x%x %s>>" % [
|
||
|
self.class.name, self.object_id, self.full_cmd,
|
||
|
@name, @desc, @subs.keys.join(', '), @aliases.keys.join(', '), @parent.class.name, @parent.class.object_id, @parent.full_cmd
|
||
|
]
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def initialize progname, desc
|
||
|
@subs = Sub.new self, progname, desc
|
||
|
end
|
||
|
|
||
|
def full_cmd
|
||
|
[]
|
||
|
end
|
||
|
|
||
|
def _full_cmd post
|
||
|
post
|
||
|
end
|
||
|
|
||
|
def sub *a, &exe
|
||
|
@subs.sub *a, &exe
|
||
|
end
|
||
|
|
||
|
def cmd *a, &exe
|
||
|
@subs.cmd *a, &exe
|
||
|
end
|
||
|
|
||
|
def call *a
|
||
|
@subs.call *a
|
||
|
end
|
||
|
|
||
|
def help *args
|
||
|
@subs.help *args
|
||
|
end
|
||
|
|
||
|
def [] k
|
||
|
@subs[k]
|
||
|
end
|
||
|
end
|