yawpa/lib/yawpa.rb

120 lines
3.5 KiB
Ruby

require "yawpa/version"
# Example options configuration:
# {
# version: {},
# verbose: {aliases: ['-v']},
# get: {nargs: 1},
# set: {nargs: 2},
# }
module Yawpa
class UnknownOptionException < Exception; end
class InvalidArgumentsException < Exception; end
module_function
def parse(params, options)
options = _massage_options(options)
opts = {}
args = []
i = 0
while i < params.length
param = params[i]
if param =~ /^--([^=]+)(?:=(.+))?$/
param_name, val = $1, $2
if options[param_name].nil?
raise UnknownOptionException.new("Unknown option '#{param_name}'")
end
opt_config = options[param_name]
param_key = opt_config[:key]
if opt_config[:nargs].last == 0
opts[param_key] = true
else
opts[param_key] = []
opts[param_key] << val if val
if opts[param_key].length < opt_config[:nargs].last
gathered = _gather(i + 1, opt_config[:nargs].last - opts[param_key].length, params)
i += gathered.length
opts[param_key] += gathered
if opts[param_key].length < opt_config[:nargs].first
raise InvalidArgumentsException.new("Not enough arguments supplied for option '#{param_key}'")
end
end
end
elsif param =~ /^-(.+)$/
short_flags = $1
short_idx = 0
while short_idx < short_flags.length
opt_config = _find_opt_config_by_short_name(options, short_flags[short_idx])
if opt_config.nil?
raise UnknownOptionException.new("Unknown option '-#{short_flags[short_idx]}'")
end
param_key = opt_config[:key]
if opt_config[:nargs].last == 0
opts[param_key] = true
else
opts[param_key] = []
if short_idx + 1 < short_flags.length
opts[param_key] << short_flags[short_idx + 1, short_flags.length]
end
if opts[param_key].length < opt_config[:nargs].last
gathered = _gather(i + 1, opt_config[:nargs].last - opts[param_key].length, params)
i += gathered.length
opts[param_key] += gathered
if opts[param_key].length < opt_config[:nargs].first
raise InvalidArgumentsException.new("Not enough arguments supplied for option '#{param_key}'")
end
end
break
end
short_idx += 1
end
else
args << params[i]
end
i += 1
end
# Condense 1-element arrays of option values to just the element itself
opts.each_key do |k|
if opts[k].class == Array and opts[k].length == 1
opts[k] = opts[k].first
end
end
return [opts, args]
end
def _gather(start_idx, max, params)
result = []
index = start_idx
loop do
break if index >= params.length
break if params[index][0] == '-'
result << params[index]
index += 1
break if result.length == max
end
result
end
def _massage_options(options)
{}.tap do |newopts|
options.each_pair do |k, v|
newkey = k.to_s
newopts[newkey] = {key: k}
nargs = v[:nargs] || 0
nargs = (nargs..nargs) if nargs.class == Fixnum
newopts[newkey][:nargs] = nargs
newopts[newkey][:short] = v[:short] || ''
end
end
end
def _find_opt_config_by_short_name(options, short_name)
options.each_pair do |k, v|
return v if v[:short] == short_name
end
nil
end
end