Files
news.gitea.happylizard.me/bin/gems/facets-3.1.0/lib/standard/facets/argvector.rb
T
pizza2d1 4cee170d66
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s
Add bin and edit workflow
2026-09-16 13:11:16 -06:00

344 lines
7.9 KiB
Ruby

# = ArgVector
#
# == Synopsis
#
# ArgVector provides a very simple means of parsing
# command line arguments.
#
# Unlike other more complex libs this provides only
# the most basic and standard parsing functionality.
# In many cases that's all one really needs.
#
# Usage is straight foward. Simply instantiate the
# class and query it for the particular "views" of
# the command line you want.
#
# cargs = ArgVector.new("-a foo -b=2")
#
# cargs.parameters #=> [['foo'],{'a'=>true,'b'=>'2'}]
# cargs.flags #=> ['a']
# cargs.preoptions #=> {'a'=>true}
# cargs.preflags #=> ['a']
# cargs.subcommand #=> ['foo', [], {'b'=>'2'}]
#
# == Authors
#
# * Thomas Sawyer
#
# == Copying
#
# Copyright (c) 2006 Thomas Sawyer
#
# Ruby License
#
# This module is free software. You may use, modify, and/or
# redistribute this software under the same terms as Ruby.
#
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE.
require 'shellwords'
# = Argvector
#
# Argvector provides a very simple means of parsing
# command line arguments.
#
# Unlike other more complex libs this provides only
# the most basic and standard parsing functionality.
# In many cases that's all one really needs.
#
# Usage is straight foward. Simply instantiate the
# class and query it for the particular "views" of
# the command line you want.
#
# cargs = Argvector.new("-a foo -b=2")
#
# cargs.parameters #=> [['foo'],{'a'=>true,'b'=>'2'}]
# cargs.flags #=> ['a']
# cargs.preoptions #=> {'a'=>true}
# cargs.preflags #=> ['a']
# cargs.subcommand #=> ['foo', [], {'b'=>'2'}]
#
class Argvector
def self.parameters(*args)
new.parameters(*args)
end
attr :line
attr :argv
attr :arity
##attr :opts
##alias :flags :options
# Takes the command line string (or array) and options.
# Options have flags and end with a hash of option arity.
#
def initialize(line=nil, arity=nil)
@line, @argv = parse_line(line)
@arity = parse_arity(arity||{})
parse
end
public
# Returns operand array.
def operands
@operands
end
# deprecate alias ?
alias arguments operands
# Returns options hash.
def options
@options
end
# Returns [operands, options], which is good for plugging
# directly into a method.
def parameters
return @operands, @options
end
# Return flags, which are true options.
def flags
f = []
@options.each do |k, v|
if TrueClass===v or FalseClass===v # not that it's ever false
f << k
end
end
return f
end
# Assumes the first operand is a "subcommand" and
# returns it and the argments following it as
# parameters.
def subcommand_with_parameters
opts, args = *parse_preoptions(argv)
cmd = args.shift
subargs = self.class.new(args, @arity)
return [cmd, *subargs.parameters]
end
alias subcommand subcommand_with_parameters
#
def subcommand_with_preoptions
pre, args = *parse_preoptions(argv)
cmd = args.shift
subargs = self.class.new(args, @arity)
args, opts = *subargs.parameters
return [cmd, args, pre.merge(opts)]
end
# Assumes the first operand is a "subcommand" and
# returns it and the argments following it as
# another Arguments object.
#
# TODO: This probably should be called 'subcommand'.
#
def subcommand_with_arguments
opts, args = *parse_preoptions(argv)
cmd = args.shift
subargs = self.class.new(args, @arity)
return cmd, subargs
end
# Returns a hash of options that occur before
# the first operand. This works well with
# subcommand to get the main command's options.
#
# line = "--trace stamp --file VERSION"
# cargs = Argvector.new(line)
# opts = cargs.preoptions
# opts #=> {"trace"=>true}
#
def preoptions
preopts, remainder = *parse_preoptions(argv)
return preopts
end
# Same as +flags+ but only returns flags in the
# preoptions.
def preflags
preopts, remainder = *parse_preoptions(argv)
f = []
preopts.each do |k, v|
if TrueClass===v or FalseClass===v # not that it's ever false
f << k
end
end
return f
end
# Like parameters but without allowing for duplicate options.
def parameters_without_duplicates
opts = {}
@options.each do |k,v|
if Array===v
opts[k] = v[0]
else
opts[k] = v
end
end
return @operands, opts
end
#private
# Basic parser partitions the command line into options and
# operands. Options are converted to a hash and the two
# parts are returned.
#
# line = "--trace stamp --file=VERSION"
# argv = Argvector.new(line)
#
# args, keys = *argv.parse
#
# args #=> ["stamp"]
# keys #=> {"trace"=>true, "file"=>"VERSION"}
#
def parse
args = assoc_options(argv) #, *opts_arity)
opts, opds = args.partition{ |a| Array === a }
@operands = opds
@options = format_options(opts)
return @operands, @options
end
# First pass parser to split the command line into an
# array using Shellwords, if not already so divided.
def parse_line(line=nil)
if line
case line
when String
argv = Shellwords.shellwords(line)
else
argv = line.to_ary.dup
line = argv.join(' ')
end
else
argv = ARGV.dup
line = argv.join(' ')
end
return line, argv
end
## Ensure opts are a uniform.
##
##def clean_opts( opts )
## opts2 = opts.collect{ |o| o.to_sym }
## opts2 = opts2 & [:simple, :repeat] # valid options only
## return opts2
##end
# Ensure arity is uniform.
def parse_arity(arity)
arity2 = {}
arity.each{ |k,v| arity2[k.to_s] = v.to_i }
return arity2
end
# Parse preoptions. A "preoption" is one that
# occurs before the first operans (if any).
def parse_preoptions(args)
##args = args.dup
args = multi_flag(args) #unless opts.include?(:simple)
flags = []
while args.first =~ /^-/
key = args.shift
key.sub!(/^-{1,2}/,'')
if key.index('=')
key, val = key.split('=')
elsif a = arity[key]
val = args.slice!(0,a)
val = val.first if a == 1
else
val = true
end
flags << [key, val]
end
flags = format_options(flags)
return flags, args
end
# Parse flags takes the command line and transforms it such that
# flags (eg. -x and --x) are elemental associative arrays.
#
# line = "--foo hello --try=this"
# argv = Argvector.new(line)
#
# args = line.split(/\s/)
# argv.assoc_options(args) #=> [ ["foo",true], "hello", ["try","this"] ]
#
def assoc_options(args)
##args = args.dup
args = multi_flag(args) #unless opts.include?(:simple)
i = 0
while i < args.size
arg = args[i]
case arg
when /^-/
arg = arg.sub(/^-{1,2}/,'')
if arg.index('=')
key, val = arg.split('=')
args[i] = [key, val||true]
elsif arity.key?(arg)
cnt = arity[arg]
key = arg
val = args[i+1,cnt]
args[i,cnt+1] = [[key, *val]]
i += (cnt - 1)
else
key = arg
args[i] = [key,true]
end
end
i += 1
end
return args
end
# Split single letter option groupings into separate options.
# ie. -xyz => -x -y -z
def multi_flag(args=nil)
args ||= argv
args.collect { |arg|
if md = /^-(\w{2,})/.match( arg )
md[1].split(//).collect { |c| "-#{c}" }
else
arg.dup
end
}.flatten
end
# Format flag options. This converts the associative array of
# options/flags into a hash. Repeat options will be placed in arrays.
def format_options(assoc_options)
opts = {}
assoc_options.each do |k,v|
if opts.key?(k)
opts[k] = [opts[k]].flatten << v
else
opts[k] = v
end
end
return opts
end
end
ArgVector = Argvector