# You are allowed to redistribute it and sell it, alone or is a part of
# another product.
+# Management of options on the command line
+module opts
+
# Super class of all option's class
-class Option
+abstract class Option
# Names for the option (including long and short ones)
readable var _names: Array[String]
# Human readable description of the option
readable var _helptext: String
+ # Gathering errors during parsing
+ readable var _errors: Array[String]
+
# Is this option mandatory?
readable writable var _mandatory: Bool
+ # Has this option been read?
+ readable var _read:Bool
+
# Current value of this option
writable var _value: nullable VALUE
end
_helptext = help
_mandatory = false
+ _read = false
_default_value = default
_value = default
+ _errors = new Array[String]
end
# Add new aliases for this option
end
# Consume parameters for this option
- protected fun read_param(it: Iterator[String]) is abstract
+ protected fun read_param(it: Iterator[String])
+ do
+ _read = true
+ end
end
class OptionText
init(help: String, names: String...) do init_opt(help, false, names)
- redef fun read_param(it) do value = true
+ redef fun read_param(it)
+ do
+ super
+ value = true
+ end
end
class OptionCount
init(help: String, names: String...) do init_opt(help, 0, names)
- redef fun read_param(it) do value += 1
+ redef fun read_param(it)
+ do
+ super
+ value += 1
+ end
end
-# Option with one mandatory parameter
-class OptionParameter
+# Option with one parameter (mandatory by default)
+abstract class OptionParameter
super Option
protected fun convert(str: String): VALUE is abstract
+ # Is the parameter mandatory?
+ readable writable var _parameter_mandatory: Bool
+
redef fun read_param(it)
do
- if it.is_ok then
+ super
+ if it.is_ok and it.item.chars.first != '-' then
value = convert(it.item)
it.next
else
- # TODO: What to do?
+ if _parameter_mandatory then
+ _errors.add("Parameter expected for option {names.first}.")
+ end
end
end
- init init_opt(h, d, n) do super
+ init init_opt(h, d, n)
+ do
+ super
+ _parameter_mandatory = true
+ end
end
class OptionString
redef fun convert(str)
do
var id = _values.index_of(str)
+ if id == -1 then
+ var e = "Unrecognized value for option {_names.join(", ")}.\n"
+ e += "Expected values are: {_values.join(", ")}."
+ _errors.add(e)
+ end
return id
end
class OptionContext
readable var _options: Array[Option]
readable var _rest: Array[String]
+ readable var _errors: Array[String]
var _optmap: Map[String, Option]
var parseargs = true
build
var rest = _rest
-
+
while parseargs and it.is_ok do
var str = it.item
if str == "--" then
rest.add_all(it.to_a)
parseargs = false
else
- if _optmap.has_key(str) then
- var opt = _optmap[str]
- it.next
- opt.read_param(it)
+ # We're looking for packed short options
+ if str.last_index_of('-') == 0 and str.length > 2 then
+ var next_called = false
+ for i in [1..str.length] do
+ var short_opt = "-" + str.chars[i].to_s
+ if _optmap.has_key(short_opt) then
+ var option = _optmap[short_opt]
+ if option isa OptionParameter then
+ it.next
+ next_called = true
+ end
+ option.read_param(it)
+ end
+ end
+ if not next_called then it.next
else
- rest.add(it.item)
- it.next
+ if _optmap.has_key(str) then
+ var opt = _optmap[str]
+ it.next
+ opt.read_param(it)
+ else
+ rest.add(it.item)
+ it.next
+ end
end
end
end
+
+ for opt in _options do
+ if opt.mandatory and not opt.read then
+ _errors.add("Mandatory option {opt.names.join(", ")} not found.")
+ end
+ end
end
fun add_option(opts: Option...)
_options = new Array[Option]
_optmap = new HashMap[String, Option]
_rest = new Array[String]
+ _errors = new Array[String]
end
private fun build
end
end
end
+
+ fun get_errors: Array[String]
+ do
+ var errors: Array[String] = new Array[String]
+
+ errors.add_all(_errors)
+
+ for o in _options do
+ for e in o.errors do
+ errors.add(e)
+ end
+ end
+
+ return errors
+ end
end