flag: allow to define flags with abbreviation

* flag: allow to check the count of the free args to produce an error

* flag: allow to define flags with abbreviation

flags could be defined to use a single char as abbreviation like
'--output' or '-o'
'--help' or '-h'
pull/1511/head
Marco Böttcher 2019-08-07 16:52:10 +02:00 committed by Alexander Medvednikov
parent 1270e8a9f2
commit c924a6cf00
2 changed files with 83 additions and 24 deletions

View File

@ -49,6 +49,7 @@ module flag
struct Flag { struct Flag {
pub: pub:
name string // name as it appears on command line name string // name as it appears on command line
abbr byte // shortcut
usage string // help message usage string // help message
val_desc string // something like '<arg>' that appears in usage val_desc string // something like '<arg>' that appears in usage
} }
@ -94,10 +95,11 @@ pub fn (fs mut FlagParser) skip_executable() {
} }
// private helper to register a flag // private helper to register a flag
fn (fs mut FlagParser) add_flag(n, u, vd string) { fn (fs mut FlagParser) add_flag(n string, a byte, u, vd string) {
fs.flags << Flag{ fs.flags << Flag{
name: n, name: n,
usage: u abbr: a,
usage: u,
val_desc: vd val_desc: vd
} }
} }
@ -111,10 +113,10 @@ fn (fs mut FlagParser) add_flag(n, u, vd string) {
// //
// - the name, usage are registered // - the name, usage are registered
// - found arguments and corresponding values are removed from args list // - found arguments and corresponding values are removed from args list
fn (fs mut FlagParser) parse_value(n string) ?string { fn (fs mut FlagParser) parse_value(n string, ab byte) ?string {
c := '--$n' c := '--$n'
for i, a in fs.args { for i, a in fs.args {
if a == c { if a == c || (a.len == 2 && a[1] == ab) {
if fs.args.len > i+1 && fs.args[i+1].left(2) != '--' { if fs.args.len > i+1 && fs.args[i+1].left(2) != '--' {
val := fs.args[i+1] val := fs.args[i+1]
fs.args.delete(i+1) fs.args.delete(i+1)
@ -138,10 +140,10 @@ fn (fs mut FlagParser) parse_value(n string) ?string {
// special: it is allowed to define bool flags without value // special: it is allowed to define bool flags without value
// -> '--flag' is parsed as true // -> '--flag' is parsed as true
// -> '--flag' is equal to '--flag=true' // -> '--flag' is equal to '--flag=true'
fn (fs mut FlagParser) parse_bool_value(n string) ?string { fn (fs mut FlagParser) parse_bool_value(n string, ab byte) ?string {
c := '--$n' c := '--$n'
for i, a in fs.args { for i, a in fs.args {
if a == c { if a == c || (a.len == 2 && a[1] == ab) {
if fs.args.len > i+1 && (fs.args[i+1] in ['true', 'false']) { if fs.args.len > i+1 && (fs.args[i+1] in ['true', 'false']) {
val := fs.args[i+1] val := fs.args[i+1]
fs.args.delete(i+1) fs.args.delete(i+1)
@ -166,15 +168,41 @@ fn (fs mut FlagParser) parse_bool_value(n string) ?string {
// the value is returned (true/false) // the value is returned (true/false)
// else // else
// the default value is returned // the default value is returned
// version with abbreviation
//TODO error handling for invalid string to bool conversion //TODO error handling for invalid string to bool conversion
pub fn (fs mut FlagParser) bool(n string, v bool, u string) bool { pub fn (fs mut FlagParser) bool_(n string, a byte, v bool, u string) bool {
fs.add_flag(n, u, '') fs.add_flag(n, a, u, '')
parsed := fs.parse_bool_value(n) or { parsed := fs.parse_bool_value(n, a) or {
return v return v
} }
return parsed == 'true' return parsed == 'true'
} }
// defining and parsing a bool flag
// if defined
// the value is returned (true/false)
// else
// the default value is returned
//TODO error handling for invalid string to bool conversion
pub fn (fs mut FlagParser) bool(n string, v bool, u string) bool {
return fs.bool_(n, `\0`, v, u)
}
// defining and parsing an int flag
// if defined
// the value is returned (int)
// else
// the default value is returned
// version with abbreviation
//TODO error handling for invalid string to int conversion
pub fn (fs mut FlagParser) int_(n string, a byte, i int, u string) int {
fs.add_flag(n, a, u, '<int>')
parsed := fs.parse_value(n, a) or {
return i
}
return parsed.int()
}
// defining and parsing an int flag // defining and parsing an int flag
// if defined // if defined
// the value is returned (int) // the value is returned (int)
@ -182,11 +210,22 @@ pub fn (fs mut FlagParser) bool(n string, v bool, u string) bool {
// the default value is returned // the default value is returned
//TODO error handling for invalid string to int conversion //TODO error handling for invalid string to int conversion
pub fn (fs mut FlagParser) int(n string, i int, u string) int { pub fn (fs mut FlagParser) int(n string, i int, u string) int {
fs.add_flag(n, u, '<int>') return fs.int_(n, `\0`, i, u)
parsed := fs.parse_value(n) or {
return i
} }
return parsed.int()
// defining and parsing a flaot flag
// if defined
// the value is returned (float)
// else
// the default value is returned
// version with abbreviation
//TODO error handling for invalid string to float conversion
pub fn (fs mut FlagParser) float_(n string, a byte, f f32, u string) f32 {
fs.add_flag(n, a, u, '<float>')
parsed := fs.parse_value(n, a) or {
return f
}
return parsed.f32()
} }
// defining and parsing a flaot flag // defining and parsing a flaot flag
@ -196,11 +235,21 @@ pub fn (fs mut FlagParser) int(n string, i int, u string) int {
// the default value is returned // the default value is returned
//TODO error handling for invalid string to float conversion //TODO error handling for invalid string to float conversion
pub fn (fs mut FlagParser) float(n string, f f32, u string) f32 { pub fn (fs mut FlagParser) float(n string, f f32, u string) f32 {
fs.add_flag(n, u, '<float>') return fs.float_(n, `\0`, f, u)
parsed := fs.parse_value(n) or {
return f
} }
return parsed.f32()
// defining and parsing a string flag
// if defined
// the value is returned (string)
// else
// the default value is returned
// version with abbreviation
pub fn (fs mut FlagParser) string_(n string, a byte, v, u string) string {
fs.add_flag(n, a, u, '<arg>')
parsed := fs.parse_value(n, a) or {
return v
}
return parsed
} }
// defining and parsing a string flag // defining and parsing a string flag
@ -209,14 +258,9 @@ pub fn (fs mut FlagParser) float(n string, f f32, u string) f32 {
// else // else
// the default value is returned // the default value is returned
pub fn (fs mut FlagParser) string(n, v, u string) string { pub fn (fs mut FlagParser) string(n, v, u string) string {
fs.add_flag(n, u, '<arg>') return fs.string_(n, `\0`, v, u)
parsed := fs.parse_value(n) or {
return v
}
return parsed
} }
// add an additional check for free arguments
// this will cause an error in finalize() if free args are out of range // this will cause an error in finalize() if free args are out of range
// (min, ..., max) // (min, ..., max)
pub fn (fs mut FlagParser) limit_free_args(min, max int) { pub fn (fs mut FlagParser) limit_free_args(min, max int) {
@ -247,7 +291,8 @@ pub fn (fs FlagParser) usage() string {
} else { } else {
SPACE.right(flag_desc.len) SPACE.right(flag_desc.len)
} }
use += '$flag_desc$space$f.usage\n' abbr_desc := if f.abbr == `\0` { '' } else { ' -${tos(f.abbr, 1)}\n' }
use += '$abbr_desc$flag_desc$space$f.usage\n'
} }
} }

View File

@ -231,3 +231,17 @@ fn test_could_expect_no_free_args() {
} }
assert args.len < 0 // expect an error and need to use args assert args.len < 0 // expect an error and need to use args
} }
fn test_allow_abreviations() {
mut fp := flag.new_flag_parser(['-v', '-o', 'some_file', '-i', '42', '-f', '2.0'])
v := fp.bool_('version', `v`, false, '')
o := fp.string_('output', `o`, 'empty', '')
i := fp.int_('count', `i`, 0, '')
f := fp.float_('value', `f`, 0.0, '')
assert v && o == 'some_file' && i == 42 && f == 2.0
u := fp.usage()
assert u.contains(' -v') && u.contains(' -o') && u.contains(' -i') && u.contains(' -f')
}