mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 23:28:48 +00:00
Merge pull request #5304 from Feoramund/clarify-flags-variadic
Clarify `core:flags` variadic behaviors
This commit is contained in:
@@ -19,7 +19,7 @@ SUBTAG_NAME :: "name"
|
|||||||
SUBTAG_POS :: "pos"
|
SUBTAG_POS :: "pos"
|
||||||
SUBTAG_REQUIRED :: "required"
|
SUBTAG_REQUIRED :: "required"
|
||||||
SUBTAG_HIDDEN :: "hidden"
|
SUBTAG_HIDDEN :: "hidden"
|
||||||
SUBTAG_VARIADIC :: "variadic"
|
SUBTAG_MANIFOLD :: "manifold"
|
||||||
SUBTAG_FILE :: "file"
|
SUBTAG_FILE :: "file"
|
||||||
SUBTAG_PERMS :: "perms"
|
SUBTAG_PERMS :: "perms"
|
||||||
SUBTAG_INDISTINCT :: "indistinct"
|
SUBTAG_INDISTINCT :: "indistinct"
|
||||||
@@ -28,7 +28,7 @@ TAG_USAGE :: "usage"
|
|||||||
|
|
||||||
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
|
UNDOCUMENTED_FLAG :: "<This flag has not been documented yet.>"
|
||||||
|
|
||||||
INTERNAL_VARIADIC_FLAG :: "varg"
|
INTERNAL_OVERFLOW_FLAG :: #config(ODIN_CORE_FLAGS_OVERFLOW_FLAG, "overflow")
|
||||||
|
|
||||||
RESERVED_HELP_FLAG :: "help"
|
RESERVED_HELP_FLAG :: "help"
|
||||||
RESERVED_HELP_FLAG_SHORT :: "h"
|
RESERVED_HELP_FLAG_SHORT :: "h"
|
||||||
|
|||||||
+16
-4
@@ -20,6 +20,17 @@ The format is similar to the Odin binary's way of handling compiler flags.
|
|||||||
-<map>:<key>=<value> set map[key] to value
|
-<map>:<key>=<value> set map[key] to value
|
||||||
|
|
||||||
|
|
||||||
|
Unhandled Arguments:
|
||||||
|
|
||||||
|
All unhandled positional arguments are placed into the `overflow` field on a
|
||||||
|
struct, if it exists. In UNIX-style parsing, the existence of a `--` on the
|
||||||
|
command line will also pass all arguments afterwards into this field.
|
||||||
|
|
||||||
|
If desired, the name of the field may be changed from `overflow` to any string
|
||||||
|
by setting the `ODIN_CORE_FLAGS_OVERFLOW_FLAG` compile-time config option with
|
||||||
|
`-define:ODIN_CORE_FLAGS_OVERFLOW_FLAG=<name>`.
|
||||||
|
|
||||||
|
|
||||||
Struct Tags:
|
Struct Tags:
|
||||||
|
|
||||||
Users of the `core:encoding/json` package may be familiar with using tags to
|
Users of the `core:encoding/json` package may be familiar with using tags to
|
||||||
@@ -32,7 +43,7 @@ Under the `args` tag, there are the following subtags:
|
|||||||
- `pos=N`: place positional argument `N` into this flag.
|
- `pos=N`: place positional argument `N` into this flag.
|
||||||
- `hidden`: hide this flag from the usage documentation.
|
- `hidden`: hide this flag from the usage documentation.
|
||||||
- `required`: cause verification to fail if this argument is not set.
|
- `required`: cause verification to fail if this argument is not set.
|
||||||
- `variadic`: take all remaining arguments when set, UNIX-style only.
|
- `manifold=N`: take several arguments at once, UNIX-style only.
|
||||||
- `file`: for `os.Handle` types, file open mode.
|
- `file`: for `os.Handle` types, file open mode.
|
||||||
- `perms`: for `os.Handle` types, file open permissions.
|
- `perms`: for `os.Handle` types, file open permissions.
|
||||||
- `indistinct`: allow the setting of distinct types by their base type.
|
- `indistinct`: allow the setting of distinct types by their base type.
|
||||||
@@ -47,8 +58,9 @@ you want to require 3 and only 3 arguments in a dynamic array, you would
|
|||||||
specify `required=3<4`.
|
specify `required=3<4`.
|
||||||
|
|
||||||
|
|
||||||
`variadic` may be given a number (`variadic=N`) above 1 to limit how many extra
|
`manifold` may be given a number (`manifold=N`) above 1 to limit how many extra
|
||||||
arguments it consumes.
|
arguments it consumes at once. If this number is not specified, it will take as
|
||||||
|
many arguments as can be converted to the underlying element type.
|
||||||
|
|
||||||
|
|
||||||
`file` determines the file open mode for an `os.Handle`.
|
`file` determines the file open mode for an `os.Handle`.
|
||||||
@@ -160,7 +172,7 @@ at parse time.
|
|||||||
--flag
|
--flag
|
||||||
--flag=argument
|
--flag=argument
|
||||||
--flag argument
|
--flag argument
|
||||||
--flag argument repeating-argument
|
--flag argument (manifold-argument)
|
||||||
|
|
||||||
`-flag` may also be substituted for `--flag`.
|
`-flag` may also be substituted for `--flag`.
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import "core:os"
|
|||||||
|
|
||||||
Parse_Error_Reason :: enum {
|
Parse_Error_Reason :: enum {
|
||||||
None,
|
None,
|
||||||
// An extra positional argument was given, and there is no `varg` field.
|
// An extra positional argument was given, and there is no `overflow` field.
|
||||||
Extra_Positional,
|
Extra_Positional,
|
||||||
// The underlying type does not support the string value it is being set to.
|
// The underlying type does not support the string value it is being set to.
|
||||||
Bad_Value,
|
Bad_Value,
|
||||||
|
|||||||
@@ -107,14 +107,14 @@ main :: proc() {
|
|||||||
|
|
||||||
// assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`,
|
// assignments: map[string]u8 `args:"name=assign" usage:"Number of jobs per worker."`,
|
||||||
|
|
||||||
// (Variadic) Only available in UNIX style:
|
// (Manifold) Only available in UNIX style:
|
||||||
|
|
||||||
// bots: [dynamic]string `args:"variadic=2,required"`,
|
// bots: [dynamic]string `args:"manifold=2,required"`,
|
||||||
|
|
||||||
verbose: bool `usage:"Show verbose output."`,
|
verbose: bool `usage:"Show verbose output."`,
|
||||||
debug: bool `args:"hidden" usage:"print debug info"`,
|
debug: bool `args:"hidden" usage:"print debug info"`,
|
||||||
|
|
||||||
varg: [dynamic]string `usage:"Any extra arguments go here."`,
|
overflow: [dynamic]string `usage:"Any extra arguments go here."`,
|
||||||
}
|
}
|
||||||
|
|
||||||
opt: Options
|
opt: Options
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ push_positional :: #force_no_inline proc (model: ^$T, parser: ^Parser, arg: stri
|
|||||||
field, index, has_pos_assigned := get_field_by_pos(model, pos)
|
field, index, has_pos_assigned := get_field_by_pos(model, pos)
|
||||||
|
|
||||||
if !has_pos_assigned {
|
if !has_pos_assigned {
|
||||||
when intrinsics.type_has_field(T, INTERNAL_VARIADIC_FLAG) {
|
when intrinsics.type_has_field(T, INTERNAL_OVERFLOW_FLAG) {
|
||||||
// Add it to the fallback array.
|
// Add it to the fallback array.
|
||||||
field = reflect.struct_field_by_name(T, INTERNAL_VARIADIC_FLAG)
|
field = reflect.struct_field_by_name(T, INTERNAL_OVERFLOW_FLAG)
|
||||||
} else {
|
} else {
|
||||||
return Parse_Error {
|
return Parse_Error {
|
||||||
.Extra_Positional,
|
.Extra_Positional,
|
||||||
@@ -117,8 +117,8 @@ set_unix_flag :: proc(model: ^$T, parser: ^Parser, name: string) -> (future_args
|
|||||||
case runtime.Type_Info_Dynamic_Array:
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
future_args = 1
|
future_args = 1
|
||||||
if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
if tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||||
if length, is_variadic := get_struct_subtag(tag, SUBTAG_VARIADIC); is_variadic {
|
if length, is_manifold := get_struct_subtag(tag, SUBTAG_MANIFOLD); is_manifold {
|
||||||
// Variadic arrays may specify how many arguments they consume at once.
|
// Manifold arrays may specify how many arguments they consume at once.
|
||||||
// Otherwise, they take everything that's left.
|
// Otherwise, they take everything that's left.
|
||||||
if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
|
if value, value_ok := strconv.parse_u64_of_base(length, 10); value_ok {
|
||||||
future_args = cast(int)value
|
future_args = cast(int)value
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ parse_one_unix_arg :: proc(model: ^$T, parser: ^Parser, arg: string) -> (
|
|||||||
// `--`, and only `--`.
|
// `--`, and only `--`.
|
||||||
// Everything from now on will be treated as an argument.
|
// Everything from now on will be treated as an argument.
|
||||||
future_args = max(int)
|
future_args = max(int)
|
||||||
current_flag = INTERNAL_VARIADIC_FLAG
|
current_flag = INTERNAL_OVERFLOW_FLAG
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS); has_pos {
|
pos_str, has_pos := get_struct_subtag(args_tag, SUBTAG_POS)
|
||||||
|
if has_pos {
|
||||||
#partial switch specific_type_info in field.type.variant {
|
#partial switch specific_type_info in field.type.variant {
|
||||||
case runtime.Type_Info_Map:
|
case runtime.Type_Info_Map:
|
||||||
fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.",
|
fmt.panicf("%T.%s has `%s` defined, and this does not make sense on a map type.",
|
||||||
@@ -79,7 +80,7 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
|||||||
fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
|
fmt.assertf(!reflect.is_boolean(field.type), "%T.%s is a required boolean. This is disallowed.",
|
||||||
model_type, field.name, loc = loc)
|
model_type, field.name, loc = loc)
|
||||||
|
|
||||||
fmt.assertf(field.name != INTERNAL_VARIADIC_FLAG, "%T.%s is defined as required. This is disallowed.",
|
fmt.assertf(field.name != INTERNAL_OVERFLOW_FLAG, "%T.%s is defined as required. This is disallowed.",
|
||||||
model_type, field.name, loc = loc)
|
model_type, field.name, loc = loc)
|
||||||
|
|
||||||
if len(requirement) > 0 {
|
if len(requirement) > 0 {
|
||||||
@@ -109,24 +110,28 @@ validate_structure :: proc(model_type: $T, style: Parsing_Style, loc := #caller_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if length, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
|
if length, is_manifold := get_struct_subtag(args_tag, SUBTAG_MANIFOLD); is_manifold {
|
||||||
|
fmt.assertf(!has_pos,
|
||||||
|
"%T.%s has both `%s` and `%s` defined. This is disallowed.\n\tSuggestion: Use a dynamic array field named `%s` to accept unspecified positional arguments.",
|
||||||
|
model_type, field.name, SUBTAG_POS, SUBTAG_MANIFOLD, INTERNAL_OVERFLOW_FLAG, loc = loc)
|
||||||
|
|
||||||
if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
|
if value, parse_ok := strconv.parse_u64_of_base(length, 10); parse_ok {
|
||||||
fmt.assertf(value > 0,
|
fmt.assertf(value > 0,
|
||||||
"%T.%s has `%s` set to %i. It must be greater than zero.",
|
"%T.%s has `%s` set to %i. It must be greater than zero.",
|
||||||
model_type, field.name, value, SUBTAG_VARIADIC, loc = loc)
|
model_type, field.name, value, SUBTAG_MANIFOLD, loc = loc)
|
||||||
fmt.assertf(value != 1,
|
fmt.assertf(value != 1,
|
||||||
"%T.%s has `%s` set to 1. This has no effect.",
|
"%T.%s has `%s` set to 1. This is equivalent to not defining `%s`.",
|
||||||
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
model_type, field.name, SUBTAG_MANIFOLD, SUBTAG_MANIFOLD, loc = loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
#partial switch specific_type_info in field.type.variant {
|
#partial switch specific_type_info in field.type.variant {
|
||||||
case runtime.Type_Info_Dynamic_Array:
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
fmt.assertf(style != .Odin,
|
fmt.assertf(style != .Odin,
|
||||||
"%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
|
"%T.%s has `%s` defined, but this only makes sense in UNIX-style parsing mode.",
|
||||||
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
model_type, field.name, SUBTAG_MANIFOLD, loc = loc)
|
||||||
case:
|
case:
|
||||||
fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
|
fmt.panicf("%T.%s has `%s` defined, but this only makes sense on dynamic arrays.",
|
||||||
model_type, field.name, SUBTAG_VARIADIC, loc = loc)
|
model_type, field.name, SUBTAG_MANIFOLD, loc = loc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ package flags
|
|||||||
Parsing_Style :: enum {
|
Parsing_Style :: enum {
|
||||||
// Odin-style: `-flag`, `-flag:option`, `-map:key=value`
|
// Odin-style: `-flag`, `-flag:option`, `-map:key=value`
|
||||||
Odin,
|
Odin,
|
||||||
// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument repeating-argument`
|
// UNIX-style: `-flag` or `--flag`, `--flag=argument`, `--flag argument (manifold-argument)`
|
||||||
Unix,
|
Unix,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ parse :: proc(
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .Unix:
|
case .Unix:
|
||||||
// Support for `-flag argument (repeating-argument ...)`
|
// Support for `-flag argument (manifold-argument ...)`
|
||||||
future_args: int
|
future_args: int
|
||||||
current_flag: string
|
current_flag: string
|
||||||
|
|
||||||
|
|||||||
+15
-15
@@ -30,18 +30,18 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
|||||||
is_positional: bool,
|
is_positional: bool,
|
||||||
is_required: bool,
|
is_required: bool,
|
||||||
is_boolean: bool,
|
is_boolean: bool,
|
||||||
is_variadic: bool,
|
is_manifold: bool,
|
||||||
variadic_length: int,
|
manifold_length: int,
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ...
|
// POSITIONAL+REQUIRED, POSITIONAL, REQUIRED, NON_REQUIRED+NON_POSITIONAL, ...
|
||||||
//
|
//
|
||||||
sort_flags :: proc(i, j: Flag) -> slice.Ordering {
|
sort_flags :: proc(i, j: Flag) -> slice.Ordering {
|
||||||
// `varg` goes to the end.
|
// `overflow` goes to the end.
|
||||||
if i.name == INTERNAL_VARIADIC_FLAG {
|
if i.name == INTERNAL_OVERFLOW_FLAG {
|
||||||
return .Greater
|
return .Greater
|
||||||
} else if j.name == INTERNAL_VARIADIC_FLAG {
|
} else if j.name == INTERNAL_OVERFLOW_FLAG {
|
||||||
return .Less
|
return .Less
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,10 +120,10 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
|||||||
flag.is_required = true
|
flag.is_required = true
|
||||||
flag.required_min, flag.required_max, _ = parse_requirements(requirement)
|
flag.required_min, flag.required_max, _ = parse_requirements(requirement)
|
||||||
}
|
}
|
||||||
if length_str, is_variadic := get_struct_subtag(args_tag, SUBTAG_VARIADIC); is_variadic {
|
if length_str, is_manifold := get_struct_subtag(args_tag, SUBTAG_MANIFOLD); is_manifold {
|
||||||
flag.is_variadic = true
|
flag.is_manifold = true
|
||||||
if length, parse_ok := strconv.parse_u64_of_base(length_str, 10); parse_ok {
|
if length, parse_ok := strconv.parse_u64_of_base(length_str, 10); parse_ok {
|
||||||
flag.variadic_length = cast(int)length
|
flag.manifold_length = cast(int)length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,15 +147,15 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
|||||||
case runtime.Type_Info_Dynamic_Array:
|
case runtime.Type_Info_Dynamic_Array:
|
||||||
requirement_spec := describe_array_requirements(flag)
|
requirement_spec := describe_array_requirements(flag)
|
||||||
|
|
||||||
if flag.is_variadic || flag.name == INTERNAL_VARIADIC_FLAG {
|
if flag.is_manifold || flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||||
if flag.variadic_length == 0 {
|
if flag.manifold_length == 0 {
|
||||||
flag.type_description = fmt.tprintf("<%v, ...>%s",
|
flag.type_description = fmt.tprintf("<%v, ...>%s",
|
||||||
specific_type_info.elem.id,
|
specific_type_info.elem.id,
|
||||||
requirement_spec)
|
requirement_spec)
|
||||||
} else {
|
} else {
|
||||||
flag.type_description = fmt.tprintf("<%v, %i at once>%s",
|
flag.type_description = fmt.tprintf("<%v, %i at once>%s",
|
||||||
specific_type_info.elem.id,
|
specific_type_info.elem.id,
|
||||||
flag.variadic_length,
|
flag.manifold_length,
|
||||||
requirement_spec)
|
requirement_spec)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -177,7 +177,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if flag.name == INTERNAL_VARIADIC_FLAG {
|
if flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||||
flag.full_length = len(flag.type_description)
|
flag.full_length = len(flag.type_description)
|
||||||
} else if flag.is_boolean {
|
} else if flag.is_boolean {
|
||||||
flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description)
|
flag.full_length = len(flag_prefix) + len(flag.name) + len(flag.type_description)
|
||||||
@@ -201,13 +201,13 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
|||||||
strings.write_string(&builder, program)
|
strings.write_string(&builder, program)
|
||||||
|
|
||||||
for flag in visible_flags {
|
for flag in visible_flags {
|
||||||
if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_VARIADIC_FLAG) {
|
if keep_it_short && !(flag.is_required || flag.is_positional || flag.name == INTERNAL_OVERFLOW_FLAG) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
strings.write_byte(&builder, ' ')
|
strings.write_byte(&builder, ' ')
|
||||||
|
|
||||||
if flag.name == INTERNAL_VARIADIC_FLAG {
|
if flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||||
strings.write_string(&builder, "...")
|
strings.write_string(&builder, "...")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -252,7 +252,7 @@ write_usage :: proc(out: io.Writer, data_type: typeid, program: string = "", sty
|
|||||||
|
|
||||||
strings.write_byte(&builder, '\t')
|
strings.write_byte(&builder, '\t')
|
||||||
|
|
||||||
if flag.name == INTERNAL_VARIADIC_FLAG {
|
if flag.name == INTERNAL_OVERFLOW_FLAG {
|
||||||
strings.write_string(&builder, flag.type_description)
|
strings.write_string(&builder, flag.type_description)
|
||||||
} else {
|
} else {
|
||||||
strings.write_string(&builder, flag_prefix)
|
strings.write_string(&builder, flag_prefix)
|
||||||
|
|||||||
@@ -454,44 +454,44 @@ test_arrays :: proc(t: ^testing.T) {
|
|||||||
@(test)
|
@(test)
|
||||||
test_varargs :: proc(t: ^testing.T) {
|
test_varargs :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
varg: [dynamic]string,
|
overflow: [dynamic]string,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
args := [?]string { "abc", "foo", "bar" }
|
args := [?]string { "abc", "foo", "bar" }
|
||||||
result := flags.parse(&s, args[:])
|
result := flags.parse(&s, args[:])
|
||||||
defer delete(s.varg)
|
defer delete(s.overflow)
|
||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
testing.expect_value(t, len(s.varg), 3)
|
testing.expect_value(t, len(s.overflow), 3)
|
||||||
|
|
||||||
if len(s.varg) < 3 {
|
if len(s.overflow) < 3 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
testing.expect_value(t, s.varg[0], "abc")
|
testing.expect_value(t, s.overflow[0], "abc")
|
||||||
testing.expect_value(t, s.varg[1], "foo")
|
testing.expect_value(t, s.overflow[1], "foo")
|
||||||
testing.expect_value(t, s.varg[2], "bar")
|
testing.expect_value(t, s.overflow[2], "bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_mixed_varargs :: proc(t: ^testing.T) {
|
test_mixed_varargs :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
input: string `args:"pos=0"`,
|
input: string `args:"pos=0"`,
|
||||||
varg: [dynamic]string,
|
overflow: [dynamic]string,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
args := [?]string { "abc", "foo", "bar" }
|
args := [?]string { "abc", "foo", "bar" }
|
||||||
result := flags.parse(&s, args[:])
|
result := flags.parse(&s, args[:])
|
||||||
defer delete(s.varg)
|
defer delete(s.overflow)
|
||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
testing.expect_value(t, len(s.varg), 2)
|
testing.expect_value(t, len(s.overflow), 2)
|
||||||
|
|
||||||
if len(s.varg) < 2 {
|
if len(s.overflow) < 2 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
testing.expect_value(t, s.input, "abc")
|
testing.expect_value(t, s.input, "abc")
|
||||||
testing.expect_value(t, s.varg[0], "foo")
|
testing.expect_value(t, s.overflow[0], "foo")
|
||||||
testing.expect_value(t, s.varg[1], "bar")
|
testing.expect_value(t, s.overflow[1], "bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
@@ -718,23 +718,23 @@ test_tags_required_limit_max :: proc(t: ^testing.T) {
|
|||||||
test_tags_pos_out_of_order :: proc(t: ^testing.T) {
|
test_tags_pos_out_of_order :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
a: int `args:"pos=2"`,
|
a: int `args:"pos=2"`,
|
||||||
varg: [dynamic]int,
|
overflow: [dynamic]int,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
args := [?]string { "1", "2", "3", "4" }
|
args := [?]string { "1", "2", "3", "4" }
|
||||||
result := flags.parse(&s, args[:])
|
result := flags.parse(&s, args[:])
|
||||||
defer delete(s.varg)
|
defer delete(s.overflow)
|
||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
testing.expect_value(t, len(s.varg), 3)
|
testing.expect_value(t, len(s.overflow), 3)
|
||||||
|
|
||||||
if len(s.varg) < 3 {
|
if len(s.overflow) < 3 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
testing.expect_value(t, s.a, 3)
|
testing.expect_value(t, s.a, 3)
|
||||||
testing.expect_value(t, s.varg[0], 1)
|
testing.expect_value(t, s.overflow[0], 1)
|
||||||
testing.expect_value(t, s.varg[1], 2)
|
testing.expect_value(t, s.overflow[1], 2)
|
||||||
testing.expect_value(t, s.varg[2], 4)
|
testing.expect_value(t, s.overflow[2], 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
@@ -899,7 +899,7 @@ test_pos_nonoverlap :: proc(t: ^testing.T) {
|
|||||||
@(test)
|
@(test)
|
||||||
test_pos_many_args :: proc(t: ^testing.T) {
|
test_pos_many_args :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
varg: [dynamic]int,
|
overflow: [dynamic]int,
|
||||||
a: int `args:"pos=0,required"`,
|
a: int `args:"pos=0,required"`,
|
||||||
b: int `args:"pos=64,required"`,
|
b: int `args:"pos=64,required"`,
|
||||||
c: int `args:"pos=66,required"`,
|
c: int `args:"pos=66,required"`,
|
||||||
@@ -908,7 +908,7 @@ test_pos_many_args :: proc(t: ^testing.T) {
|
|||||||
s: S
|
s: S
|
||||||
|
|
||||||
args: [dynamic]string
|
args: [dynamic]string
|
||||||
defer delete(s.varg)
|
defer delete(s.overflow)
|
||||||
|
|
||||||
for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) }
|
for i in 0 ..< 130 { append(&args, fmt.aprintf("%i", 1 + i)) }
|
||||||
defer {
|
defer {
|
||||||
@@ -922,14 +922,14 @@ test_pos_many_args :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
|
|
||||||
testing.expect_value(t, s.a, 1)
|
testing.expect_value(t, s.a, 1)
|
||||||
for i in 1 ..< 63 { testing.expect_value(t, s.varg[i], 2 + i) }
|
for i in 1 ..< 63 { testing.expect_value(t, s.overflow[i], 2 + i) }
|
||||||
testing.expect_value(t, s.b, 65)
|
testing.expect_value(t, s.b, 65)
|
||||||
testing.expect_value(t, s.varg[63], 66)
|
testing.expect_value(t, s.overflow[63], 66)
|
||||||
testing.expect_value(t, s.c, 67)
|
testing.expect_value(t, s.c, 67)
|
||||||
testing.expect_value(t, s.varg[64], 68)
|
testing.expect_value(t, s.overflow[64], 68)
|
||||||
testing.expect_value(t, s.varg[65], 69)
|
testing.expect_value(t, s.overflow[65], 69)
|
||||||
testing.expect_value(t, s.varg[66], 70)
|
testing.expect_value(t, s.overflow[66], 70)
|
||||||
for i in 67 ..< 126 { testing.expect_value(t, s.varg[i], 4 + i) }
|
for i in 67 ..< 126 { testing.expect_value(t, s.overflow[i], 4 + i) }
|
||||||
testing.expect_value(t, s.d, 130)
|
testing.expect_value(t, s.d, 130)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -966,9 +966,9 @@ test_unix :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_unix_variadic :: proc(t: ^testing.T) {
|
test_unix_manifold :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
a: [dynamic]int `args:"variadic"`,
|
a: [dynamic]int `args:"manifold"`,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
|
|
||||||
@@ -989,9 +989,9 @@ test_unix_variadic :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_unix_variadic_limited :: proc(t: ^testing.T) {
|
test_unix_manifold_limited :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
a: [dynamic]int `args:"variadic=2"`,
|
a: [dynamic]int `args:"manifold=2"`,
|
||||||
b: int,
|
b: int,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
@@ -1012,6 +1012,110 @@ test_unix_variadic_limited :: proc(t: ^testing.T) {
|
|||||||
testing.expect_value(t, s.b, 3)
|
testing.expect_value(t, s.b, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_unix_two_manifold_limited :: proc(t: ^testing.T) {
|
||||||
|
S :: struct {
|
||||||
|
a: [dynamic]int `args:"manifold=2"`,
|
||||||
|
b: [dynamic]int `args:"manifold=2"`,
|
||||||
|
c: int,
|
||||||
|
}
|
||||||
|
s: S
|
||||||
|
|
||||||
|
args := [?]string { "-a", "11", "101", "-b", "3", "7", "-c", "9" }
|
||||||
|
|
||||||
|
result := flags.parse(&s, args[:], .Unix)
|
||||||
|
defer {
|
||||||
|
delete(s.a)
|
||||||
|
delete(s.b)
|
||||||
|
}
|
||||||
|
testing.expect_value(t, result, nil)
|
||||||
|
testing.expect_value(t, len(s.a), 2)
|
||||||
|
testing.expect_value(t, len(s.b), 2)
|
||||||
|
|
||||||
|
if len(s.a) < 2 || len(s.b) < 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testing.expect_value(t, s.a[0], 11)
|
||||||
|
testing.expect_value(t, s.a[1], 101)
|
||||||
|
testing.expect_value(t, s.b[0], 3)
|
||||||
|
testing.expect_value(t, s.b[1], 7)
|
||||||
|
testing.expect_value(t, s.c, 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_unix_two_manifold_string :: proc(t: ^testing.T) {
|
||||||
|
// The expected behavior of a manifold flag is to consume all arguments as
|
||||||
|
// fitting for the element type.
|
||||||
|
S :: struct {
|
||||||
|
a: [dynamic]string `args:"manifold"`,
|
||||||
|
b: [dynamic]string `args:"manifold"`,
|
||||||
|
c: int,
|
||||||
|
}
|
||||||
|
s: S
|
||||||
|
|
||||||
|
args := [?]string { "-a", "11", "101", "-b", "3", "7", "-c", "9" }
|
||||||
|
|
||||||
|
result := flags.parse(&s, args[:], .Unix)
|
||||||
|
defer {
|
||||||
|
delete(s.a)
|
||||||
|
delete(s.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
testing.expect_value(t, result, nil)
|
||||||
|
testing.expect_value(t, len(s.a), 7)
|
||||||
|
testing.expect_value(t, len(s.b), 0)
|
||||||
|
|
||||||
|
if len(s.a) != 7 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testing.expect_value(t, s.a[0], "11")
|
||||||
|
testing.expect_value(t, s.a[1], "101")
|
||||||
|
testing.expect_value(t, s.a[2], "-b")
|
||||||
|
testing.expect_value(t, s.a[3], "3")
|
||||||
|
testing.expect_value(t, s.a[4], "7")
|
||||||
|
testing.expect_value(t, s.a[5], "-c")
|
||||||
|
testing.expect_value(t, s.a[6], "9")
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
test_unix_two_manifold_int :: proc(t: ^testing.T) {
|
||||||
|
// If a manifold flag encounters an argument that it cannot convert to the
|
||||||
|
// element type, then this is an error.
|
||||||
|
S :: struct {
|
||||||
|
a: [dynamic]int `args:"manifold"`,
|
||||||
|
b: [dynamic]int `args:"manifold"`,
|
||||||
|
c: int,
|
||||||
|
}
|
||||||
|
s: S
|
||||||
|
|
||||||
|
args := [?]string { "-a", "11", "101", "-b", "3", "7", "-c", "9" }
|
||||||
|
|
||||||
|
result := flags.parse(&s, args[:], .Unix)
|
||||||
|
defer {
|
||||||
|
delete(s.a)
|
||||||
|
delete(s.b)
|
||||||
|
}
|
||||||
|
|
||||||
|
err, ok := result.(flags.Parse_Error)
|
||||||
|
testing.expectf(t, ok, "unexpected result: %v", result)
|
||||||
|
if ok {
|
||||||
|
testing.expect_value(t, err.reason, flags.Parse_Error_Reason.Bad_Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is expected that arguments which pass will still be available.
|
||||||
|
testing.expect_value(t, len(s.a), 2)
|
||||||
|
testing.expect_value(t, len(s.b), 0)
|
||||||
|
|
||||||
|
if len(s.a) != 2 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
testing.expect_value(t, s.a[0], 11)
|
||||||
|
testing.expect_value(t, s.a[1], 101)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_unix_positional :: proc(t: ^testing.T) {
|
test_unix_positional :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
@@ -1029,10 +1133,10 @@ test_unix_positional :: proc(t: ^testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_unix_positional_with_variadic :: proc(t: ^testing.T) {
|
test_unix_positional_with_manifold :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
varg: [dynamic]int,
|
overflow: [dynamic]int,
|
||||||
v: [dynamic]int `args:"variadic"`,
|
v: [dynamic]int `args:"manifold"`,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
|
|
||||||
@@ -1040,18 +1144,18 @@ test_unix_positional_with_variadic :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
result := flags.parse(&s, args[:], .Unix)
|
result := flags.parse(&s, args[:], .Unix)
|
||||||
defer {
|
defer {
|
||||||
delete(s.varg)
|
delete(s.overflow)
|
||||||
delete(s.v)
|
delete(s.v)
|
||||||
}
|
}
|
||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
testing.expect_value(t, len(s.varg), 1)
|
testing.expect_value(t, len(s.overflow), 1)
|
||||||
testing.expect_value(t, len(s.v), 2)
|
testing.expect_value(t, len(s.v), 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
test_unix_double_dash_variadic :: proc(t: ^testing.T) {
|
test_unix_double_dash_varargs :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
varg: [dynamic]string,
|
overflow: [dynamic]string,
|
||||||
i: int,
|
i: int,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
@@ -1060,19 +1164,19 @@ test_unix_double_dash_variadic :: proc(t: ^testing.T) {
|
|||||||
|
|
||||||
result := flags.parse(&s, args[:], .Unix)
|
result := flags.parse(&s, args[:], .Unix)
|
||||||
defer {
|
defer {
|
||||||
delete(s.varg)
|
delete(s.overflow)
|
||||||
}
|
}
|
||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
testing.expect_value(t, len(s.varg), 3)
|
testing.expect_value(t, len(s.overflow), 3)
|
||||||
testing.expect_value(t, s.i, 3)
|
testing.expect_value(t, s.i, 3)
|
||||||
|
|
||||||
if len(s.varg) != 3 {
|
if len(s.overflow) != 3 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
testing.expect_value(t, s.varg[0], "hellope")
|
testing.expect_value(t, s.overflow[0], "hellope")
|
||||||
testing.expect_value(t, s.varg[1], "-i")
|
testing.expect_value(t, s.overflow[1], "-i")
|
||||||
testing.expect_value(t, s.varg[2], "5")
|
testing.expect_value(t, s.overflow[2], "5")
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
@@ -1096,17 +1200,17 @@ test_unix_no_value :: proc(t: ^testing.T) {
|
|||||||
@(test)
|
@(test)
|
||||||
test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) {
|
test_if_dynamic_cstrings_get_freed :: proc(t: ^testing.T) {
|
||||||
S :: struct {
|
S :: struct {
|
||||||
varg: [dynamic]cstring,
|
overflow: [dynamic]cstring,
|
||||||
}
|
}
|
||||||
s: S
|
s: S
|
||||||
|
|
||||||
args := [?]string { "Hellope", "world!" }
|
args := [?]string { "Hellope", "world!" }
|
||||||
result := flags.parse(&s, args[:])
|
result := flags.parse(&s, args[:])
|
||||||
defer {
|
defer {
|
||||||
for v in s.varg {
|
for v in s.overflow {
|
||||||
delete(v)
|
delete(v)
|
||||||
}
|
}
|
||||||
delete(s.varg)
|
delete(s.overflow)
|
||||||
}
|
}
|
||||||
testing.expect_value(t, result, nil)
|
testing.expect_value(t, result, nil)
|
||||||
}
|
}
|
||||||
@@ -1324,7 +1428,7 @@ very nicely.
|
|||||||
debug: bool `args:"hidden" usage:"print debug info"`,
|
debug: bool `args:"hidden" usage:"print debug info"`,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
|
||||||
varg: [dynamic]string,
|
overflow: [dynamic]string,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
@@ -1337,7 +1441,7 @@ very nicely.
|
|||||||
@(test)
|
@(test)
|
||||||
test_usage_write_unix :: proc(t: ^testing.T) {
|
test_usage_write_unix :: proc(t: ^testing.T) {
|
||||||
Expected_Output :: `Usage:
|
Expected_Output :: `Usage:
|
||||||
varg required-number [number] [name] --bars --bots --foos --gadgets --variadic-flag --widgets [--array] [--count] [--greek] [--verbose] ...
|
overflow required-number [number] [name] --bars --bots --foos --gadgets --manifold-flag --widgets [--array] [--count] [--greek] [--verbose] ...
|
||||||
Flags:
|
Flags:
|
||||||
--required-number <int>, required | some number
|
--required-number <int>, required | some number
|
||||||
--number <int> | some other number
|
--number <int> | some other number
|
||||||
@@ -1349,7 +1453,7 @@ Flags:
|
|||||||
--bots <string>, at least 1 | <This flag has not been documented yet.>
|
--bots <string>, at least 1 | <This flag has not been documented yet.>
|
||||||
--foos <string>, between 2 and 3 | <This flag has not been documented yet.>
|
--foos <string>, between 2 and 3 | <This flag has not been documented yet.>
|
||||||
--gadgets <string>, at least 1 | <This flag has not been documented yet.>
|
--gadgets <string>, at least 1 | <This flag has not been documented yet.>
|
||||||
--variadic-flag <int, ...>, at least 2 | <This flag has not been documented yet.>
|
--manifold-flag <int, ...>, at least 2 | <This flag has not been documented yet.>
|
||||||
--widgets <string>, at most 2 | <This flag has not been documented yet.>
|
--widgets <string>, at most 2 | <This flag has not been documented yet.>
|
||||||
|
|
|
|
||||||
--array <rune>, multiple | <This flag has not been documented yet.>
|
--array <rune>, multiple | <This flag has not been documented yet.>
|
||||||
@@ -1378,7 +1482,7 @@ very nicely.
|
|||||||
greek: Custom_Enum,
|
greek: Custom_Enum,
|
||||||
|
|
||||||
array: [dynamic]rune,
|
array: [dynamic]rune,
|
||||||
variadic_flag: [dynamic]int `args:"variadic,required=2"`,
|
manifold_flag: [dynamic]int `args:"manifold,required=2"`,
|
||||||
|
|
||||||
gadgets: [dynamic]string `args:"required=1"`,
|
gadgets: [dynamic]string `args:"required=1"`,
|
||||||
widgets: [dynamic]string `args:"required=<3"`,
|
widgets: [dynamic]string `args:"required=<3"`,
|
||||||
@@ -1389,12 +1493,12 @@ very nicely.
|
|||||||
debug: bool `args:"hidden" usage:"print debug info"`,
|
debug: bool `args:"hidden" usage:"print debug info"`,
|
||||||
verbose: bool,
|
verbose: bool,
|
||||||
|
|
||||||
varg: [dynamic]string,
|
overflow: [dynamic]string,
|
||||||
}
|
}
|
||||||
|
|
||||||
builder := strings.builder_make()
|
builder := strings.builder_make()
|
||||||
defer strings.builder_destroy(&builder)
|
defer strings.builder_destroy(&builder)
|
||||||
writer := strings.to_stream(&builder)
|
writer := strings.to_stream(&builder)
|
||||||
flags.write_usage(writer, S, "varg", .Unix)
|
flags.write_usage(writer, S, "overflow", .Unix)
|
||||||
testing.expect_value(t, strings.to_string(builder), Expected_Output)
|
testing.expect_value(t, strings.to_string(builder), Expected_Output)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user