Fix positional nested parameter array parsing#70
Conversation
Normalize `multiple` CLI args to arrays on first value insert, so `Vec<T>` fields no longer parse as scalar when only one token is provided. This fixes inconsistent behavior where single trailing positional values failed with “expected sequence start” while multiple values succeeded. - propagate `is_multiple` through positional, long/short flag, and parent adoption paths - ensure first occurrence of multi-valued args is stored as a 1-item array - keep existing subcommand/sequence behavior intact - verifies with subcommand + flattened payload integration tests for single and multiple positional scalars
There was a problem hiding this comment.
Pull request overview
Fixes a CLI parsing edge case where positional Vec<T> fields inside a flattened subcommand payload would fail when exactly one item was provided (issue #69).
Changes:
- Add coverage for flattened-subcommand positional
Vec<String>parsing with single vs multiple tokens. - Teach the CLI layer to insert an array on first assignment when the target arg schema is
multiple(soVec<T>deserializes correctly even with a single value). - Propagate
multiplemetadata through parent-flag (“adoption agency”) lookups and flag parsing helpers.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| crates/figue/tests/integration/sequence.rs | Adds regression tests for flattened subcommand positional Vec<String> (single + multiple tokens). |
| crates/figue/src/layers/cli.rs | Adds is_multiple tracking and ensures single occurrences of multiple args are stored as arrays. |
Comments suppressed due to low confidence (1)
crates/figue/src/layers/cli.rs:983
insert_value_tonow takesis_multiple, but the occupied-entry path still always accumulates into an array even whenis_multipleis false. That means repeating a non-multiple arg/flag (e.g.--port 1 --port 2) will silently produce aConfigValue::Arrayand later fail deserialization with a confusing "expected scalar"-style error. Consider usingis_multiplehere to either (a) overwrite the previous value (last-one-wins) or (b) emit a dedicated diagnostic for duplicate non-multiple args, and only accumulate whenis_multipleis true.
indexmap::map::Entry::Occupied(mut entry) => {
// Accumulate into array for repeated flags
let existing = entry.get_mut();
if let ConfigValue::Array(arr) = existing {
arr.value.push(value);
} else {
// Convert to array with both values
let placeholder = ConfigValue::Null(Sourced {
value: (),
span: None,
provenance: None,
});
let old = core::mem::replace(existing, placeholder);
*existing = ConfigValue::Array(Sourced {
value: vec![old, value],
span: None,
provenance: None,
});
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…de struct and passing it into parse_flag_value_simple.
Use last-value-wins for repeated non-multiple args in CLI insertion logic, while preserving accumulation for multiple fields. Add an integration regression test for repeated --port to ensure non-multiple named args stay scalar and parse cleanly.
|
Existing behaviour #[test]
fn test_duplicate_non_multiple_named_last_value_wins() {
#[derive(Facet, Debug)]
struct Args {
#[facet(args::named)]
port: u16,
}
let args: Args = figue::from_slice(&["--port", "1", "--port", "2"]).unwrap();
assert_eq!(args.port, 2);
}it is an error when a non-array parameter is specified multiple times, which aligns with my expectations updating my changes to ensure this remains the case |
When a non-multiple named argument is provided more than once, report an explicit duplicate-argument diagnostic instead of coercing values into a sequence or relying on deserialization errors. Keep multi-value argument accumulation unchanged and add an integration test for repeated --port.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Report duplicate non-multiple argument errors at a stable span covering the repeated flag and its value (rather than the current token pointer). Update integration coverage to assert the duplicate --port diagnostic anchors at the second flag.
Keep span/provenance on ConfigValue::Array wrappers when inserting multiple args, including scalar-to-array rewrites and appends. This improves provenance tracking and keeps diagnostics anchored to meaningful CLI spans.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
A nested command structure caused me problems, see #69
The following would fail to parse if there was exactly one item specified
The existing behaviour would succeed if multiple items were passed, however
Disclaimer: this fix was created by GPT-5.3 Codex.
Snapshot tests dislike windows lol