diff options
Diffstat (limited to '')
| -rw-r--r-- | clap.zig | 556 |
1 files changed, 244 insertions, 312 deletions
| @@ -19,6 +19,23 @@ pub fn Clap(comptime Result: type) type { | |||
| 19 | command: Command, | 19 | command: Command, |
| 20 | defaults: Result, | 20 | defaults: Result, |
| 21 | 21 | ||
| 22 | pub fn init(defaults: &const Result) Self { | ||
| 23 | return Self { | ||
| 24 | .program_name = "", | ||
| 25 | .author = "", | ||
| 26 | .version = "", | ||
| 27 | .about = "", | ||
| 28 | .command = Command.init(""), | ||
| 29 | .defaults = *defaults, | ||
| 30 | }; | ||
| 31 | } | ||
| 32 | |||
| 33 | pub fn with(parser: &const Self, comptime field: []const u8, value: var) Self { | ||
| 34 | var res = *parser; | ||
| 35 | @field(res, field) = value; | ||
| 36 | return res; | ||
| 37 | } | ||
| 38 | |||
| 22 | pub fn parse(comptime clap: &const Self, arguments: []const []const u8) !Result { | 39 | pub fn parse(comptime clap: &const Self, arguments: []const []const u8) !Result { |
| 23 | return parseCommand(CommandList { .command = clap.command, .prev = null }, clap.defaults, arguments); | 40 | return parseCommand(CommandList { .command = clap.command, .prev = null }, clap.defaults, arguments); |
| 24 | } | 41 | } |
| @@ -108,12 +125,18 @@ pub fn Clap(comptime Result: type) type { | |||
| 108 | if (option.short != null) continue; | 125 | if (option.short != null) continue; |
| 109 | if (option.long != null) continue; | 126 | if (option.long != null) continue; |
| 110 | 127 | ||
| 111 | try option.parse(&result, arg); | 128 | if (option.takes_value) |parser| { |
| 129 | try parser.parse(&@field(result, option.field), arg); | ||
| 130 | } else { | ||
| 131 | @field(result, option.field) = true; | ||
| 132 | } | ||
| 133 | |||
| 112 | required = newRequired(option, required, required_index); | 134 | required = newRequired(option, required, required_index); |
| 113 | break :success; | 135 | break :success; |
| 114 | } | 136 | } |
| 115 | }, | 137 | }, |
| 116 | Arg.Kind.Short => { | 138 | Arg.Kind.Short => { |
| 139 | const arg_len = arg.len; | ||
| 117 | if (arg.len == 0) return error.FoundShortOptionWithNoName; | 140 | if (arg.len == 0) return error.FoundShortOptionWithNoName; |
| 118 | short_arg_loop: for (arg[0..arg.len - 1]) |short_arg| { | 141 | short_arg_loop: for (arg[0..arg.len - 1]) |short_arg| { |
| 119 | var required_index = usize(0); | 142 | var required_index = usize(0); |
| @@ -121,9 +144,9 @@ pub fn Clap(comptime Result: type) type { | |||
| 121 | defer if (option.required) required_index += 1; | 144 | defer if (option.required) required_index += 1; |
| 122 | const short = option.short ?? continue; | 145 | const short = option.short ?? continue; |
| 123 | if (short_arg == short) { | 146 | if (short_arg == short) { |
| 124 | if (option.takes_value) return error.OptionMissingValue; | 147 | if (option.takes_value) |_| return error.OptionMissingValue; |
| 125 | 148 | ||
| 126 | *getFieldPtr(Result, &result, option.field) = true; | 149 | @field(result, option.field) = true; |
| 127 | required = newRequired(option, required, required_index); | 150 | required = newRequired(option, required, required_index); |
| 128 | continue :short_arg_loop; | 151 | continue :short_arg_loop; |
| 129 | } | 152 | } |
| @@ -139,11 +162,11 @@ pub fn Clap(comptime Result: type) type { | |||
| 139 | const short = option.short ?? continue; | 162 | const short = option.short ?? continue; |
| 140 | 163 | ||
| 141 | if (last_arg == short) { | 164 | if (last_arg == short) { |
| 142 | if (option.takes_value) { | 165 | if (option.takes_value) |parser| { |
| 143 | const value = after_eql ?? it.next() ?? return error.OptionMissingValue; | 166 | const value = after_eql ?? it.next() ?? return error.OptionMissingValue; |
| 144 | *getFieldPtr(Result, &result, option.field) = try strToValue(FieldType(Result, option.field), value); | 167 | try parser.parse(&@field(result, option.field), value); |
| 145 | } else { | 168 | } else { |
| 146 | *getFieldPtr(Result, &result, option.field) = true; | 169 | @field(result, option.field) = true; |
| 147 | } | 170 | } |
| 148 | 171 | ||
| 149 | required = newRequired(option, required, required_index); | 172 | required = newRequired(option, required, required_index); |
| @@ -158,11 +181,11 @@ pub fn Clap(comptime Result: type) type { | |||
| 158 | const long = option.long ?? continue; | 181 | const long = option.long ?? continue; |
| 159 | 182 | ||
| 160 | if (mem.eql(u8, arg, long)) { | 183 | if (mem.eql(u8, arg, long)) { |
| 161 | if (option.takes_value) { | 184 | if (option.takes_value) |parser| { |
| 162 | const value = after_eql ?? it.next() ?? return error.OptionMissingValue; | 185 | const value = after_eql ?? it.next() ?? return error.OptionMissingValue; |
| 163 | *getFieldPtr(Result, &result, option.field) = try strToValue(FieldType(Result, option.field), value); | 186 | try parser.parse(&@field(result, option.field), value); |
| 164 | } else { | 187 | } else { |
| 165 | *getFieldPtr(Result, &result, option.field) = true; | 188 | @field(result, option.field) = true; |
| 166 | } | 189 | } |
| 167 | 190 | ||
| 168 | required = newRequired(option, required, required_index); | 191 | required = newRequired(option, required, required_index); |
| @@ -183,106 +206,12 @@ pub fn Clap(comptime Result: type) type { | |||
| 183 | return result; | 206 | return result; |
| 184 | } | 207 | } |
| 185 | 208 | ||
| 186 | fn FieldType(comptime T: type, comptime field: []const u8) type { | ||
| 187 | var i = usize(0); | ||
| 188 | inline while (i < @memberCount(T)) : (i += 1) { | ||
| 189 | if (mem.eql(u8, @memberName(T, i), field)) | ||
| 190 | return @memberType(T, i); | ||
| 191 | } | ||
| 192 | |||
| 193 | @compileError("Field not found!"); | ||
| 194 | } | ||
| 195 | |||
| 196 | fn getFieldPtr(comptime T: type, res: &T, comptime field: []const u8) &FieldType(T, field) { | ||
| 197 | return @intToPtr(&FieldType(T, field), @ptrToInt(res) + @offsetOf(T, field)); | ||
| 198 | } | ||
| 199 | |||
| 200 | fn strToValue(comptime T: type, str: []const u8) !T { | ||
| 201 | const TypeId = builtin.TypeId; | ||
| 202 | switch (@typeId(T)) { | ||
| 203 | TypeId.Type, TypeId.Void, TypeId.NoReturn, TypeId.Pointer, | ||
| 204 | TypeId.Array, TypeId.Struct, TypeId.UndefinedLiteral, | ||
| 205 | TypeId.NullLiteral, TypeId.ErrorUnion, TypeId.ErrorSet, | ||
| 206 | TypeId.Union, TypeId.Fn, TypeId.Namespace, TypeId.Block, | ||
| 207 | TypeId.BoundFn, TypeId.ArgTuple, TypeId.Opaque, TypeId.Promise => @compileError("Type not supported!"), | ||
| 208 | |||
| 209 | TypeId.Bool => { | ||
| 210 | if (mem.eql(u8, "true", str)) | ||
| 211 | return true; | ||
| 212 | if (mem.eql(u8, "false", str)) | ||
| 213 | return false; | ||
| 214 | |||
| 215 | return error.CannotParseStringAsBool; | ||
| 216 | }, | ||
| 217 | TypeId.Int, TypeId.IntLiteral => return fmt.parseInt(T, str, 10), | ||
| 218 | TypeId.Float, TypeId.FloatLiteral => @compileError("TODO: Implement str to float"), | ||
| 219 | TypeId.Nullable => { | ||
| 220 | if (mem.eql(u8, "null", str)) | ||
| 221 | return null; | ||
| 222 | |||
| 223 | return strToValue(T.Child, str); | ||
| 224 | }, | ||
| 225 | TypeId.Enum => @compileError("TODO: Implement str to enum"), | ||
| 226 | } | ||
| 227 | } | ||
| 228 | |||
| 229 | fn newRequired(argument: &const Argument, old_required: u128, index: usize) u128 { | 209 | fn newRequired(argument: &const Argument, old_required: u128, index: usize) u128 { |
| 230 | if (argument.required) | 210 | if (argument.required) |
| 231 | return old_required & ~(u128(1) << u7(index)); | 211 | return old_required & ~(u128(1) << u7(index)); |
| 232 | 212 | ||
| 233 | return old_required; | 213 | return old_required; |
| 234 | } | 214 | } |
| 235 | |||
| 236 | pub const Builder = struct { | ||
| 237 | result: Self, | ||
| 238 | |||
| 239 | pub fn init(defaults: &const Result) Builder { | ||
| 240 | return Builder { | ||
| 241 | .result = Self { | ||
| 242 | .program_name = "", | ||
| 243 | .author = "", | ||
| 244 | .version = "", | ||
| 245 | .about = "", | ||
| 246 | .command = Command.Builder.init("").build(), | ||
| 247 | .defaults = *defaults, | ||
| 248 | } | ||
| 249 | }; | ||
| 250 | } | ||
| 251 | |||
| 252 | pub fn programName(builder: &const Builder, name: []const u8) Builder { | ||
| 253 | var res = *builder; | ||
| 254 | res.result.program_name = name; | ||
| 255 | return res; | ||
| 256 | } | ||
| 257 | |||
| 258 | pub fn author(builder: &const Builder, name: []const u8) Builder { | ||
| 259 | var res = *builder; | ||
| 260 | res.result.author = name; | ||
| 261 | return res; | ||
| 262 | } | ||
| 263 | |||
| 264 | pub fn version(builder: &const Builder, version_str: []const u8) Builder { | ||
| 265 | var res = *builder; | ||
| 266 | res.result.author = version_str; | ||
| 267 | return res; | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn about(builder: &const Builder, text: []const u8) Builder { | ||
| 271 | var res = *builder; | ||
| 272 | res.result.about = text; | ||
| 273 | return res; | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn command(builder: &const Builder, cmd: &const Command) Builder { | ||
| 277 | var res = *builder; | ||
| 278 | res.result.command = *cmd; | ||
| 279 | return res; | ||
| 280 | } | ||
| 281 | |||
| 282 | pub fn build(builder: &const Builder) Self { | ||
| 283 | return builder.result; | ||
| 284 | } | ||
| 285 | }; | ||
| 286 | }; | 215 | }; |
| 287 | } | 216 | } |
| 288 | 217 | ||
| @@ -292,242 +221,245 @@ pub const Command = struct { | |||
| 292 | arguments: []const Argument, | 221 | arguments: []const Argument, |
| 293 | sub_commands: []const Command, | 222 | sub_commands: []const Command, |
| 294 | 223 | ||
| 295 | pub const Builder = struct { | 224 | pub fn init(command_name: []const u8) Command { |
| 296 | result: Command, | 225 | return Command { |
| 226 | .field = null, | ||
| 227 | .name = command_name, | ||
| 228 | .arguments = []Argument{ }, | ||
| 229 | .sub_commands = []Command{ }, | ||
| 230 | }; | ||
| 231 | } | ||
| 297 | 232 | ||
| 298 | pub fn init(command_name: []const u8) Builder { | 233 | pub fn with(command: &const Command, comptime field: []const u8, value: var) Command { |
| 299 | return Builder { | 234 | var res = *command; |
| 300 | .result = Command { | 235 | @field(res, field) = value; |
| 301 | .field = null, | 236 | return res; |
| 302 | .name = command_name, | 237 | } |
| 303 | .arguments = []Argument{ }, | 238 | }; |
| 304 | .sub_commands = []Command{ }, | ||
| 305 | } | ||
| 306 | }; | ||
| 307 | } | ||
| 308 | 239 | ||
| 309 | pub fn field(builder: &const Builder, field_name: []const u8) Builder { | 240 | const Parser = struct { |
| 310 | var res = *builder; | 241 | const UnsafeFunction = &const void; |
| 311 | res.result.field = field_name; | ||
| 312 | return res; | ||
| 313 | } | ||
| 314 | 242 | ||
| 315 | pub fn name(builder: &const Builder, n: []const u8) Builder { | 243 | FieldType: type, |
| 316 | var res = *builder; | 244 | Errors: type, |
| 317 | res.result.name = n; | 245 | func: UnsafeFunction, |
| 318 | return res; | ||
| 319 | } | ||
| 320 | 246 | ||
| 321 | pub fn arguments(builder: &const Builder, args: []const Argument) Builder { | 247 | fn init(comptime FieldType: type, comptime Errors: type, func: parseFunc(FieldType, Errors)) Parser { |
| 322 | var res = *builder; | 248 | return Parser { |
| 323 | res.result.arguments = args; | 249 | .FieldType = FieldType, |
| 324 | return res; | 250 | .Errors = Errors, |
| 325 | } | 251 | .func = @ptrCast(UnsafeFunction, func), |
| 252 | }; | ||
| 253 | } | ||
| 326 | 254 | ||
| 327 | pub fn subCommands(builder: &const Builder, commands: []const Command) Builder { | 255 | fn parse(comptime parser: Parser, field_ptr: takePtr(parser.FieldType), arg: []const u8) parser.Errors!void { |
| 328 | var res = *builder; | 256 | return @ptrCast(parseFunc(parser.FieldType, parser.Errors), parser.func)(field_ptr, arg); |
| 329 | res.result.commands = commands; | 257 | } |
| 330 | return res; | ||
| 331 | } | ||
| 332 | 258 | ||
| 333 | pub fn build(builder: &const Builder) Command { | 259 | // TODO: This is a workaround, since we don't have pointer reform yet. |
| 334 | return builder.result; | 260 | fn takePtr(comptime T: type) type { return &T; } |
| 335 | } | 261 | |
| 336 | }; | 262 | fn parseFunc(comptime FieldType: type, comptime Errors: type) type { |
| 263 | return fn(&FieldType, []const u8) Errors!void; | ||
| 264 | } | ||
| 337 | }; | 265 | }; |
| 338 | 266 | ||
| 339 | pub const Argument = struct { | 267 | pub const Argument = struct { |
| 340 | field: []const u8, | 268 | field: []const u8, |
| 341 | help: []const u8, | 269 | help: []const u8, |
| 342 | takes_value: bool, | 270 | takes_value: ?Parser, |
| 343 | required: bool, | 271 | required: bool, |
| 344 | short: ?u8, | 272 | short: ?u8, |
| 345 | long: ?[]const u8, | 273 | long: ?[]const u8, |
| 346 | 274 | ||
| 347 | pub const Builder = struct { | 275 | pub fn field(field_name: []const u8) Argument { |
| 348 | result: Argument, | 276 | return Argument { |
| 349 | 277 | .field = field_name, | |
| 350 | pub fn init(field_name: []const u8) Builder { | 278 | .help = "", |
| 351 | return Builder { | 279 | .takes_value = null, |
| 352 | .result = Argument { | 280 | .required = false, |
| 353 | .field = field_name, | 281 | .short = null, |
| 354 | .help = "", | 282 | .long = null, |
| 355 | .takes_value = false, | 283 | }; |
| 356 | .required = false, | 284 | } |
| 357 | .short = null, | ||
| 358 | .long = null, | ||
| 359 | } | ||
| 360 | }; | ||
| 361 | } | ||
| 362 | 285 | ||
| 363 | pub fn field(builder: &const Builder, field_name: []const u8) Builder { | 286 | pub fn arg(s: []const u8) Argument { |
| 364 | var res = *builder; | 287 | return Argument { |
| 365 | res.result.field = field_name; | 288 | .field = s, |
| 366 | return res; | 289 | .help = "", |
| 367 | } | 290 | .takes_value = null, |
| 291 | .required = false, | ||
| 292 | .short = if (s.len == 1) s[0] else null, | ||
| 293 | .long = if (s.len != 1) s else null, | ||
| 294 | }; | ||
| 295 | } | ||
| 368 | 296 | ||
| 369 | pub fn help(builder: &const Builder, text: []const u8) Builder { | 297 | pub fn with(argument: &const Argument, comptime field_name: []const u8, value: var) Argument { |
| 370 | var res = *builder; | 298 | var res = *argument; |
| 371 | res.result.help = text; | 299 | @field(res, field_name) = value; |
| 372 | return res; | 300 | return res; |
| 373 | } | 301 | } |
| 302 | }; | ||
| 374 | 303 | ||
| 375 | pub fn takesValue(builder: &const Builder, takes_value: bool) Builder { | 304 | pub const parse = struct { |
| 376 | var res = *builder; | 305 | pub fn int(comptime Int: type, comptime radix: u8) Parser { |
| 377 | res.result.takes_value = takes_value; | 306 | const func = struct { |
| 378 | return res; | 307 | fn i(field_ptr: &Int, arg: []const u8) !void { |
| 379 | } | 308 | *field_ptr = try fmt.parseInt(Int, arg, radix); |
| 309 | } | ||
| 310 | }.i; | ||
| 311 | return Parser.init( | ||
| 312 | Int, | ||
| 313 | @typeOf(func).ReturnType.ErrorSet, | ||
| 314 | func | ||
| 315 | ); | ||
| 316 | } | ||
| 380 | 317 | ||
| 381 | pub fn required(builder: &const Builder, is_required: bool) Builder { | 318 | const string = Parser.init( |
| 382 | var res = *builder; | 319 | []const u8, |
| 383 | res.result.required = is_required; | 320 | error{}, |
| 384 | return res; | 321 | struct { |
| 385 | } | 322 | fn s(field_ptr: &[]const u8, arg: []const u8) (error{}!void) { |
| 323 | *field_ptr = arg; | ||
| 324 | } | ||
| 325 | }.s | ||
| 326 | ); | ||
| 327 | |||
| 328 | const boolean = Parser.init( | ||
| 329 | []const u8, | ||
| 330 | error{InvalidBoolArg}, | ||
| 331 | struct { | ||
| 332 | fn b(comptime T: type, field_ptr: &T, arg: []const u8) (error{InvalidBoolArg}!void) { | ||
| 333 | if (mem.eql(u8, arg, "true")) { | ||
| 334 | *field_ptr = true; | ||
| 335 | } else if (mem.eql(u8, arg, "false")) { | ||
| 336 | *field_ptr = false; | ||
| 337 | } else { | ||
| 338 | return error.InvalidBoolArg; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | }.b | ||
| 342 | ); | ||
| 343 | }; | ||
| 386 | 344 | ||
| 387 | pub fn short(builder: &const Builder, name: u8) Builder { | ||
| 388 | var res = *builder; | ||
| 389 | res.result.short = name; | ||
| 390 | return res; | ||
| 391 | } | ||
| 392 | 345 | ||
| 393 | pub fn long(builder: &const Builder, name: []const u8) Builder { | 346 | const Options = struct { |
| 394 | var res = *builder; | 347 | str: []const u8, |
| 395 | res.result.long = name; | 348 | int: i64, |
| 396 | return res; | 349 | uint: u64, |
| 397 | } | 350 | a: bool, |
| 351 | b: bool, | ||
| 352 | cc: bool, | ||
| 398 | 353 | ||
| 399 | pub fn build(builder: &const Builder) Argument { | 354 | pub fn with(op: &const Options, comptime field: []const u8, value: var) Options { |
| 400 | return builder.result; | 355 | var res = *op; |
| 401 | } | 356 | @field(res, field) = value; |
| 402 | }; | 357 | return res; |
| 358 | } | ||
| 403 | }; | 359 | }; |
| 404 | 360 | ||
| 405 | test "clap.parse.Example" { | 361 | const default = Options { |
| 406 | const Color = struct { | 362 | .str = "", |
| 407 | r: u8, g: u8, b: u8, max: bool | 363 | .int = 0, |
| 408 | }; | 364 | .uint = 0, |
| 365 | .a = false, | ||
| 366 | .b = false, | ||
| 367 | .cc = false, | ||
| 368 | }; | ||
| 409 | 369 | ||
| 410 | const Case = struct { args: []const []const u8, res: Color, err: ?error }; | 370 | fn testNoErr(comptime clap: &const Clap(Options), args: []const []const u8, expected: &const Options) void { |
| 411 | const cases = []Case { | 371 | const actual = clap.parse(args) catch |err| { debug.warn("{}\n", @errorName(err)); unreachable; }; |
| 412 | Case { | 372 | assert(mem.eql(u8, expected.str, actual.str)); |
| 413 | .args = [][]const u8 { "-r", "100", "-g", "100", "-b", "100", }, | 373 | assert(expected.int == actual.int); |
| 414 | .res = Color { .r = 100, .g = 100, .b = 100, .max = false }, | 374 | assert(expected.uint == actual.uint); |
| 415 | .err = null, | 375 | assert(expected.a == actual.a); |
| 416 | }, | 376 | assert(expected.b == actual.b); |
| 417 | Case { | 377 | assert(expected.cc == actual.cc); |
| 418 | .args = [][]const u8 { "--red", "100", "-g", "100", "--blue", "50", }, | 378 | } |
| 419 | .res = Color { .r = 100, .g = 100, .b = 50, .max = false }, | ||
| 420 | .err = null, | ||
| 421 | }, | ||
| 422 | Case { | ||
| 423 | .args = [][]const u8 { "--red=100", "-g=100", "--blue=50", }, | ||
| 424 | .res = Color { .r = 100, .g = 100, .b = 50, .max = false }, | ||
| 425 | .err = null, | ||
| 426 | }, | ||
| 427 | Case { | ||
| 428 | .args = [][]const u8 { "-g", "200", "--blue", "100", "--red", "100", }, | ||
| 429 | .res = Color { .r = 100, .g = 200, .b = 100, .max = false }, | ||
| 430 | .err = null, | ||
| 431 | }, | ||
| 432 | Case { | ||
| 433 | .args = [][]const u8 { "-r", "200", "-r", "255" }, | ||
| 434 | .res = Color { .r = 255, .g = 0, .b = 0, .max = false }, | ||
| 435 | .err = null, | ||
| 436 | }, | ||
| 437 | Case { | ||
| 438 | .args = [][]const u8 { "-mr", "100" }, | ||
| 439 | .res = Color { .r = 100, .g = 0, .b = 0, .max = true }, | ||
| 440 | .err = null, | ||
| 441 | }, | ||
| 442 | Case { | ||
| 443 | .args = [][]const u8 { "-mr=100" }, | ||
| 444 | .res = Color { .r = 100, .g = 0, .b = 0, .max = true }, | ||
| 445 | .err = null, | ||
| 446 | }, | ||
| 447 | Case { | ||
| 448 | .args = [][]const u8 { "-g", "200", "-b", "255" }, | ||
| 449 | .res = Color { .r = 0, .g = 0, .b = 0, .max = false }, | ||
| 450 | .err = error.RequiredArgumentWasntHandled, | ||
| 451 | }, | ||
| 452 | Case { | ||
| 453 | .args = [][]const u8 { "-p" }, | ||
| 454 | .res = Color { .r = 0, .g = 0, .b = 0, .max = false }, | ||
| 455 | .err = error.InvalidArgument, | ||
| 456 | }, | ||
| 457 | Case { | ||
| 458 | .args = [][]const u8 { "-g" }, | ||
| 459 | .res = Color { .r = 0, .g = 0, .b = 0, .max = false }, | ||
| 460 | .err = error.OptionMissingValue, | ||
| 461 | }, | ||
| 462 | Case { | ||
| 463 | .args = [][]const u8 { "-" }, | ||
| 464 | .res = Color { .r = 0, .g = 0, .b = 0, .max = false }, | ||
| 465 | .err = error.FoundShortOptionWithNoName, | ||
| 466 | }, | ||
| 467 | Case { | ||
| 468 | .args = [][]const u8 { "-rg", "100" }, | ||
| 469 | .res = Color { .r = 0, .g = 0, .b = 0, .max = false }, | ||
| 470 | .err = error.OptionMissingValue, | ||
| 471 | }, | ||
| 472 | }; | ||
| 473 | 379 | ||
| 474 | const clap = comptime Clap(Color).Builder | 380 | fn testErr(args: []const []const u8, expected: error) void { |
| 475 | .init( | 381 | if (clap.parse(case.args)) |actual| { |
| 476 | Color { | 382 | unreachable; |
| 477 | .r = 0, | 383 | } else |err| { |
| 478 | .b = 0, | 384 | assert(err == expected); |
| 479 | .g = 0, | 385 | } |
| 480 | .max = false, | 386 | } |
| 387 | |||
| 388 | test "clap.parse: short" { | ||
| 389 | @breakpoint(); | ||
| 390 | const clap = comptime Clap(Options).init(default).with("command", | ||
| 391 | Command.init("").with("arguments", | ||
| 392 | []Argument { | ||
| 393 | Argument.arg("a"), | ||
| 394 | Argument.arg("b"), | ||
| 395 | Argument.field("int") | ||
| 396 | .with("short", 'i') | ||
| 397 | .with("takes_value", parse.int(i64, 10)) | ||
| 481 | } | 398 | } |
| 482 | ) | 399 | ) |
| 483 | .command( | 400 | ); |
| 484 | Command.Builder | 401 | |
| 485 | .init("color") | 402 | testNoErr(clap, [][]const u8 { "-a" }, default.with("a", true)); |
| 486 | .arguments( | 403 | testNoErr(clap, [][]const u8 { "-a", "-b" }, default.with("a", true).with("b", true)); |
| 487 | []Argument { | 404 | testNoErr(clap, [][]const u8 { "-i=100" }, default.with("int", 100)); |
| 488 | Argument.Builder | 405 | testNoErr(clap, [][]const u8 { "-i", "100" }, default.with("int", 100)); |
| 489 | .init("r") | 406 | testNoErr(clap, [][]const u8 { "-ab" }, default.with("a", true).with("b", true)); |
| 490 | .help("The amount of red in our color") | 407 | testNoErr(clap, [][]const u8 { "-abi 100" }, default.with("a", true).with("b", true).with("int", 100)); |
| 491 | .short('r') | 408 | testNoErr(clap, [][]const u8 { "-abi=100" }, default.with("a", true).with("b", true).with("int", 100)); |
| 492 | .long("red") | 409 | } |
| 493 | .takesValue(true) | 410 | |
| 494 | .required(true) | 411 | test "clap.parse: long" { |
| 495 | .build(), | 412 | @breakpoint(); |
| 496 | Argument.Builder | 413 | const clap = comptime Clap(Options).init(default).with("command", |
| 497 | .init("g") | 414 | Command.init("").with("arguments", |
| 498 | .help("The amount of green in our color") | 415 | []Argument { |
| 499 | .short('g') | 416 | Argument.arg("cc"), |
| 500 | .long("green") | 417 | Argument.arg("int").with("takes_value", parse.int(i64, 10)), |
| 501 | .takesValue(true) | 418 | Argument.arg("uint").with("takes_value", parse.int(u64, 10)), |
| 502 | .build(), | 419 | Argument.arg("str").with("takes_value", parse.string), |
| 503 | Argument.Builder | 420 | } |
| 504 | .init("b") | ||
| 505 | .help("The amount of blue in our color") | ||
| 506 | .short('b') | ||
| 507 | .long("blue") | ||
| 508 | .takesValue(true) | ||
| 509 | .build(), | ||
| 510 | Argument.Builder | ||
| 511 | .init("max") | ||
| 512 | .help("Set all values to max") | ||
| 513 | .short('m') | ||
| 514 | .long("max") | ||
| 515 | .build(), | ||
| 516 | } | ||
| 517 | ) | ||
| 518 | .build() | ||
| 519 | ) | 421 | ) |
| 520 | .build(); | 422 | ); |
| 521 | 423 | ||
| 522 | for (cases) |case, i| { | 424 | testNoErr(clap, [][]const u8 { "--cc" }, default.with("cc", true)); |
| 523 | if (clap.parse(case.args)) |res| { | 425 | testNoErr(clap, [][]const u8 { "--int", "100" }, default.with("int", 100)); |
| 524 | assert(case.err == null); | 426 | } |
| 525 | assert(res.r == case.res.r); | 427 | |
| 526 | assert(res.g == case.res.g); | 428 | test "clap.parse: value bool" { |
| 527 | assert(res.b == case.res.b); | 429 | @breakpoint(); |
| 528 | assert(res.max == case.res.max); | 430 | const clap = comptime Clap(Options).init(default).with("command", |
| 529 | } else |err| { | 431 | Command.init("").with("arguments", |
| 530 | assert(err == ??case.err); | 432 | []Argument { |
| 531 | } | 433 | Argument.field("a"), |
| 532 | } | 434 | } |
| 435 | ) | ||
| 436 | ); | ||
| 437 | |||
| 438 | testNoErr(clap, [][]const u8 { "Hello World!" }, default.with("a", true)); | ||
| 439 | } | ||
| 440 | |||
| 441 | test "clap.parse: value str" { | ||
| 442 | @breakpoint(); | ||
| 443 | const clap = comptime Clap(Options).init(default).with("command", | ||
| 444 | Command.init("").with("arguments", | ||
| 445 | []Argument { | ||
| 446 | Argument.field("str").with("takes_value", parse.string), | ||
| 447 | } | ||
| 448 | ) | ||
| 449 | ); | ||
| 450 | |||
| 451 | testNoErr(clap, [][]const u8 { "Hello World!" }, default.with("str", "Hello World!")); | ||
| 452 | } | ||
| 453 | |||
| 454 | test "clap.parse: value int" { | ||
| 455 | @breakpoint(); | ||
| 456 | const clap = comptime Clap(Options).init(default).with("command", | ||
| 457 | Command.init("").with("arguments", | ||
| 458 | []Argument { | ||
| 459 | Argument.field("int").with("takes_value", parse.int(i64, 10)), | ||
| 460 | } | ||
| 461 | ) | ||
| 462 | ); | ||
| 463 | |||
| 464 | testNoErr(clap, [][]const u8 { "100" }, default.with("int", 100)); | ||
| 533 | } | 465 | } |