Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

flag: fix assigning to @[tail] field when no fields has been matched yet in flag.parse[T]() #22043

Merged
merged 1 commit into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 25 additions & 20 deletions vlib/flag/flag_to.v
Original file line number Diff line number Diff line change
Expand Up @@ -524,8 +524,9 @@ pub fn (mut fm FlagMapper) parse[T]() ! {
}
}

// Extract any tail according to config
// Extract any tail(s) according to config
if pos >= pos_last_flag + 1 {
trace_dbg_println('${@FN}: (tail) looking for tail match for position "${pos}"...')
pos_is_handled = pos in fm.handled_pos
if pos_is_handled {
trace_dbg_println('${@FN}: (tail) skipping position "${pos}". Already handled')
Expand All @@ -542,27 +543,27 @@ pub fn (mut fm FlagMapper) parse[T]() ! {
continue
}
if field.hints.has(.has_tail) {
if last_handled_pos := fm.handled_pos[fm.handled_pos.len - 1] {
trace_println('${@FN}: (tail) flag `${arg}` last_handled_pos: ${last_handled_pos} pos: ${pos}')
if pos == last_handled_pos + 1 {
if field.hints.has(.is_array) {
fm.array_field_map_flag[field.name] << FlagData{
raw: arg
field_name: field.name
arg: ?string(arg) // .arg is used when assigning at comptime to []XYZ
pos: pos
}
} else {
fm.field_map_flag[field.name] = FlagData{
raw: arg
field_name: field.name
arg: ?string(arg)
pos: pos
}
trace_dbg_println('${@FN}: (tail) field "${field.name}" has a tail attribute. fm.handled_pos.len: ${fm.handled_pos.len}')
last_handled_pos := fm.handled_pos[fm.handled_pos.len - 1] or { 0 }
trace_println('${@FN}: (tail) flag `${arg}` last_handled_pos: ${last_handled_pos} pos: ${pos}')
if pos == last_handled_pos + 1 || pos == pos_last_flag + 1 {
if field.hints.has(.is_array) {
fm.array_field_map_flag[field.name] << FlagData{
raw: arg
field_name: field.name
arg: ?string(arg) // .arg is used when assigning at comptime to []XYZ
pos: pos
}
} else {
fm.field_map_flag[field.name] = FlagData{
raw: arg
field_name: field.name
arg: ?string(arg)
pos: pos
}
fm.handled_pos << pos
continue
}
fm.handled_pos << pos
continue
}
}
}
Expand Down Expand Up @@ -900,6 +901,10 @@ pub fn (fm FlagMapper) to_struct[T](defaults ?T) !T {
}
result.$(field.name) = true
} $else $if field.typ is string {
trace_dbg_println('${@FN}: assigning (string) ${struct_name}.${field.name} = ${f.arg or {
'ERROR'
}
.str()}')
result.$(field.name) = f.arg or {
return error('failed appending ${f.raw} to ${field.name}')
}
Expand Down
62 changes: 62 additions & 0 deletions vlib/flag/flag_to_tail_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import flag

const posix_and_gnu_args_no_tail = ['-f']

const posix_and_gnu_args_tail = ['-f', 'tail']
const posix_and_gnu_args_tails = ['-f', 'tail0', 'tail1', 'tail2']

const skip_posix_and_gnu_args_tail = ['skipme', '-f', 'tail']
const skip_posix_and_gnu_args_tails = ['skipme', '-f', 'tail0', 'tail1', 'tail2']

struct ConfigTail {
mix bool
path string @[tail]
}

struct ConfigTails {
mix bool
paths []string @[tail]
}

fn test_flag_tail() {
// Test `@[tail]` edge-cases
config1, no_matches1 := flag.to_struct[ConfigTail](posix_and_gnu_args_tail)!
assert config1.mix == false
assert config1.path == 'tail'
assert no_matches1 == ['-f']

config2, no_matches2 := flag.to_struct[ConfigTails](posix_and_gnu_args_tails)!
assert config2.mix == false
assert config2.paths == ['tail0', 'tail1', 'tail2']
assert no_matches2 == ['-f']

config3, no_matches3 := flag.to_struct[ConfigTail](skip_posix_and_gnu_args_tail, skip: 1)!
assert config3.mix == false
assert config3.path == 'tail'
assert no_matches3 == ['-f']

config4, no_matches4 := flag.to_struct[ConfigTails](skip_posix_and_gnu_args_tails, skip: 1)!
assert config4.mix == false
assert config4.paths == ['tail0', 'tail1', 'tail2']
assert no_matches4 == ['-f']

config5, no_matches5 := flag.to_struct[ConfigTail](posix_and_gnu_args_tail, skip: 1)!
assert config5.mix == false
assert config5.path == 'tail'
assert no_matches5 == []

config6, no_matches6 := flag.to_struct[ConfigTails](posix_and_gnu_args_tails, skip: 1)!
assert config6.mix == false
assert config6.paths == ['tail0', 'tail1', 'tail2']
assert no_matches6 == []

config7, no_matches7 := flag.to_struct[ConfigTail](posix_and_gnu_args_no_tail)!
assert config7.mix == false
assert config7.path == ''
assert no_matches7 == ['-f']

config8, no_matches8 := flag.to_struct[ConfigTail](posix_and_gnu_args_no_tail, skip: 1)!
assert config8.mix == false
assert config8.path == ''
assert no_matches8 == []
}
Loading