| 1 | module cli |
| 2 | |
| 3 | pub enum FlagType { |
| 4 | bool |
| 5 | int |
| 6 | float |
| 7 | string |
| 8 | // If flag can set multiple time, use array type |
| 9 | int_array |
| 10 | float_array |
| 11 | string_array |
| 12 | } |
| 13 | |
| 14 | // Flag holds information for a command line flag. |
| 15 | // (flags are also commonly referred to as "options" or "switches") |
| 16 | // These are typically denoted in the shell by a short form `-f` and/or a long form `--flag` |
| 17 | pub struct Flag { |
| 18 | pub mut: |
| 19 | flag FlagType |
| 20 | // Name of flag |
| 21 | name string |
| 22 | // Like short option |
| 23 | abbrev string |
| 24 | // Description of flag |
| 25 | description string |
| 26 | // If the flag is added to this command and to all subcommands |
| 27 | global bool |
| 28 | // If flag is required |
| 29 | required bool |
| 30 | // Default value if no value provide by command line |
| 31 | default_value []string = [] |
| 32 | mut: |
| 33 | // Set true if flag found. |
| 34 | found bool |
| 35 | // Value of flag |
| 36 | value []string = [] |
| 37 | } |
| 38 | |
| 39 | // get_all_found returns an array of all `Flag`s found in the command parameters. |
| 40 | pub fn (flags []Flag) get_all_found() []Flag { |
| 41 | return flags.filter(it.found) |
| 42 | } |
| 43 | |
| 44 | // get_bool returns `true` if the flag is set. |
| 45 | // get_bool returns an error if the `FlagType` is not boolean. |
| 46 | pub fn (flag &Flag) get_bool() !bool { |
| 47 | if flag.flag != .bool { |
| 48 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `bool`') |
| 49 | } |
| 50 | |
| 51 | val := flag.get_value_or_default_value() |
| 52 | |
| 53 | return val.len > 0 && val[0] == 'true' |
| 54 | } |
| 55 | |
| 56 | // get_bool returns `true` if the flag specified in `name` is set. |
| 57 | // get_bool returns an error if the `FlagType` is not boolean. |
| 58 | pub fn (flags []Flag) get_bool(name string) !bool { |
| 59 | flag := flags.get(name)! |
| 60 | return flag.get_bool() |
| 61 | } |
| 62 | |
| 63 | // get_int returns the `int` value argument of the flag. |
| 64 | // get_int returns an error if the `FlagType` is not integer. |
| 65 | pub fn (flag &Flag) get_int() !int { |
| 66 | if flag.flag != .int { |
| 67 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `int`') |
| 68 | } |
| 69 | |
| 70 | val := flag.get_value_or_default_value() |
| 71 | |
| 72 | if val.len == 0 { |
| 73 | return 0 |
| 74 | } else { |
| 75 | return val[0].int() |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | // get_ints returns the array of `int` value argument of the flag specified in `name`. |
| 80 | // get_ints returns an error if the `FlagType` is not integer. |
| 81 | pub fn (flag &Flag) get_ints() ![]int { |
| 82 | if flag.flag != .int_array { |
| 83 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `int_array`') |
| 84 | } |
| 85 | |
| 86 | val := flag.get_value_or_default_value() |
| 87 | |
| 88 | if val.len == 0 { |
| 89 | return []int{} |
| 90 | } else { |
| 91 | mut values := []int{} |
| 92 | |
| 93 | for f in val { |
| 94 | values << f.int() |
| 95 | } |
| 96 | |
| 97 | return values |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | // get_int returns the `int` value argument of the flag specified in `name`. |
| 102 | // get_int returns an error if the `FlagType` is not integer. |
| 103 | pub fn (flags []Flag) get_int(name string) !int { |
| 104 | flag := flags.get(name)! |
| 105 | return flag.get_int() |
| 106 | } |
| 107 | |
| 108 | // get_ints returns the array of `int` value argument of the flag specified in `name`. |
| 109 | // get_ints returns an error if the `FlagType` is not integer. |
| 110 | pub fn (flags []Flag) get_ints(name string) ![]int { |
| 111 | flag := flags.get(name)! |
| 112 | return flag.get_ints() |
| 113 | } |
| 114 | |
| 115 | // get_float returns the `f64` value argument of the flag. |
| 116 | // get_float returns an error if the `FlagType` is not floating point. |
| 117 | pub fn (flag &Flag) get_float() !f64 { |
| 118 | if flag.flag != .float { |
| 119 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `float`') |
| 120 | } |
| 121 | |
| 122 | val := flag.get_value_or_default_value() |
| 123 | |
| 124 | if val.len == 0 { |
| 125 | return 0.0 |
| 126 | } else { |
| 127 | return val[0].f64() |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | // get_floats returns the `f64` value argument of the flag. |
| 132 | // get_floats returns an error if the `FlagType` is not floating point. |
| 133 | pub fn (flag &Flag) get_floats() ![]f64 { |
| 134 | if flag.flag != .float_array { |
| 135 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `float_array`') |
| 136 | } |
| 137 | |
| 138 | val := flag.get_value_or_default_value() |
| 139 | |
| 140 | if val.len == 0 { |
| 141 | return []f64{} |
| 142 | } else { |
| 143 | mut values := []f64{} |
| 144 | |
| 145 | for f in val { |
| 146 | values << f.f64() |
| 147 | } |
| 148 | |
| 149 | return values |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | // get_float returns the `f64` value argument of the flag specified in `name`. |
| 154 | // get_float returns an error if the `FlagType` is not floating point. |
| 155 | pub fn (flags []Flag) get_float(name string) !f64 { |
| 156 | flag := flags.get(name)! |
| 157 | return flag.get_float() |
| 158 | } |
| 159 | |
| 160 | // get_floats returns the array of `f64` value argument of the flag specified in `name`. |
| 161 | // get_floats returns an error if the `FlagType` is not floating point. |
| 162 | pub fn (flags []Flag) get_floats(name string) ![]f64 { |
| 163 | flag := flags.get(name)! |
| 164 | return flag.get_floats() |
| 165 | } |
| 166 | |
| 167 | // get_string returns the `string` value argument of the flag. |
| 168 | // get_string returns an error if the `FlagType` is not string. |
| 169 | pub fn (flag &Flag) get_string() !string { |
| 170 | if flag.flag != .string { |
| 171 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `string`') |
| 172 | } |
| 173 | |
| 174 | val := flag.get_value_or_default_value() |
| 175 | |
| 176 | if val.len == 0 { |
| 177 | return '' |
| 178 | } else { |
| 179 | return val[0] |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | // get_strings returns the array of `string` value argument of the flag. |
| 184 | // get_strings returns an error if the `FlagType` is not string. |
| 185 | pub fn (flag &Flag) get_strings() ![]string { |
| 186 | if flag.flag != .string_array { |
| 187 | return error('${flag.name}: Invalid flag type `${flag.flag}`, expected `string_array`') |
| 188 | } |
| 189 | |
| 190 | val := flag.get_value_or_default_value() |
| 191 | |
| 192 | if val.len == 0 { |
| 193 | return []string{} |
| 194 | } else { |
| 195 | return val |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | // get_string returns the `string` value argument of the flag specified in `name`. |
| 200 | // get_string returns an error if the `FlagType` is not string. |
| 201 | pub fn (flags []Flag) get_string(name string) !string { |
| 202 | flag := flags.get(name)! |
| 203 | return flag.get_string() |
| 204 | } |
| 205 | |
| 206 | // get_strings returns the `string` value argument of the flag specified in `name`. |
| 207 | // get_strings returns an error if the `FlagType` is not string. |
| 208 | pub fn (flags []Flag) get_strings(name string) ![]string { |
| 209 | flag := flags.get(name)! |
| 210 | return flag.get_strings() |
| 211 | } |
| 212 | |
| 213 | // parse parses flag values from arguments and return an array of arguments with all consumed elements removed. |
| 214 | pub fn (mut flag Flag) parse(args []string, posix_mode bool) ![]string { |
| 215 | return match true { |
| 216 | !flag.matches(args[0], posix_mode) { |
| 217 | args |
| 218 | } |
| 219 | flag.flag == .bool { |
| 220 | flag.parse_bool(args)! |
| 221 | } |
| 222 | flag.value.len > 0 && flag.flag !in [.int_array, .float_array, .string_array] { |
| 223 | error('The argument `${flag.name}` accept only one value!') |
| 224 | } |
| 225 | else { |
| 226 | flag.parse_raw(args)! |
| 227 | } |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | // matches returns `true` if first arg in `args` matches this flag. |
| 232 | fn (mut flag Flag) matches(arg string, posix_mode bool) bool { |
| 233 | prefix := if posix_mode { '--' } else { '-' } |
| 234 | return (flag.name != '' && arg == '${prefix}${flag.name}') |
| 235 | || (flag.name != '' && arg.starts_with('${prefix}${flag.name}=')) |
| 236 | || (flag.abbrev != '' && arg == '-${flag.abbrev}') |
| 237 | || (flag.abbrev != '' && arg.starts_with('-${flag.abbrev}=')) |
| 238 | } |
| 239 | |
| 240 | fn (mut flag Flag) parse_raw(args []string) ![]string { |
| 241 | if args[0].len > flag.name.len && args[0].contains('=') { |
| 242 | flag.value << args[0].split('=')[1] |
| 243 | return args[1..] |
| 244 | } else if args.len >= 2 { |
| 245 | flag.value << args[1] |
| 246 | return args[2..] |
| 247 | } |
| 248 | return error('Missing argument for `${flag.name}`') |
| 249 | } |
| 250 | |
| 251 | fn (mut flag Flag) parse_bool(args []string) ![]string { |
| 252 | if args[0].len > flag.name.len && args[0].contains('=') { |
| 253 | flag.value = [args[0].split('=')[1]] |
| 254 | return args[1..] |
| 255 | } else if args.len >= 2 { |
| 256 | if args[1] in ['true', 'false'] { |
| 257 | flag.value = [args[1]] |
| 258 | return args[2..] |
| 259 | } |
| 260 | } |
| 261 | // In fact bool cannot be multiple |
| 262 | flag.value = ['true'] |
| 263 | return args[1..] |
| 264 | } |
| 265 | |
| 266 | // get returns the `Flag` matching `name` or an error |
| 267 | // if it can't be found. |
| 268 | fn (flags []Flag) get(name string) !Flag { |
| 269 | for flag in flags { |
| 270 | if flag.name == name { |
| 271 | return flag |
| 272 | } |
| 273 | } |
| 274 | return error('Flag `${name}` not found in ${flags}') |
| 275 | } |
| 276 | |
| 277 | fn (flags []Flag) contains(name string) bool { |
| 278 | for flag in flags { |
| 279 | if flag.name == name || flag.abbrev == name { |
| 280 | return true |
| 281 | } |
| 282 | } |
| 283 | return false |
| 284 | } |
| 285 | |
| 286 | // Check if value is set by command line option. If not, return default value. |
| 287 | fn (flag &Flag) get_value_or_default_value() []string { |
| 288 | if flag.value.len == 0 && flag.default_value.len > 0 { |
| 289 | // If default value is set and no value provide, use default value. |
| 290 | return flag.default_value |
| 291 | } else { |
| 292 | return flag.value |
| 293 | } |
| 294 | } |
| 295 | |