//! Command line parsing. //! //! Each command declares the options it takes, so a typo is refused with a //! usage error instead of being ignored — an agent's mistake should be loud. module cli pub struct Options { pub: values []string // options that take a value flags []string // options that take none } pub struct Args { pub mut: positional []string values map[string]string flags map[string]bool } pub fn parse(command string, raw []string, spec Options) !Args { mut args := Args{} mut index := 0 for index < raw.len { item := raw[index] if item == '--' { args.positional << raw[index + 1..] break } if item.starts_with('--') { mut name := item[2..] mut inline := '' if at := name.index('=') { inline = name[at + 1..] name = name[..at] } if name in spec.values { mut value := inline if value == '' { index++ if index >= raw.len { return error('--${name} needs a value') } value = raw[index] } args.values[name] = value } else if name in spec.flags { if inline != '' { return error('--${name} takes no value') } args.flags[name] = true } else { return error('unknown option "--${name}" for "koh ${command}"') } } else if item.starts_with('-') && item.len > 1 { return error('unknown option "${item}" for "koh ${command}"') } else { args.positional << item } index++ } return args } pub fn (args &Args) flag(name string) bool { return args.flags[name] } pub fn (args &Args) value(name string) string { return args.values[name] or { '' } } pub fn (args &Args) value_or_none(name string) ?string { if name in args.values { return args.values[name] } return none } pub fn (args &Args) json() bool { return args.flag('json') } // positional_at returns the argument at `index`, or '' when it is missing. pub fn (args &Args) positional_at(index int) string { if index >= args.positional.len { return '' } return args.positional[index] }