Skip to content

Commit 7ed332a

Browse files
author
Krut Patel
committed
feat(tasks): Handle array defaults for variadic flags in usage spec
See jdx#7041
1 parent da78120 commit 7ed332a

File tree

4 files changed

+62
-24
lines changed

4 files changed

+62
-24
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ toml_edit = { version = "0.22", features = ["parse"] }
159159
ubi = { version = "0.8", default-features = false }
160160
url = "2"
161161
urlencoding = "2.1.3"
162-
usage-lib = { version = "2", features = ["clap", "docs"] }
162+
usage-lib = { version = "2.9", features = ["clap", "docs"] }
163163
versions = { version = "6", features = ["serde"] }
164164
vfox = { path = "crates/vfox", default-features = false }
165165
aqua-registry = { path = "crates/aqua-registry" }

e2e/tasks/test_task_usage_map_tera

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -101,22 +101,37 @@ echo "tags={{ usage.tag | join(sep='::') }}"
101101
'''
102102
EOF
103103

104-
# DISABLED: this is a bug in usage-lib.
105-
# Today, it treats the default as a string, not an array.
106-
# assert "mise run usage-tera-var-flag-join --tag foo --tag bar" "tags=foo::bar"
104+
assert "mise run usage-tera-var-flag-join --tag foo --tag bar" "tags=foo::bar"
107105

108-
# Variadic flags with defaults SHOULD be exposed as arrays in the usage map
109-
# but this is a bug in usage-lib.
106+
# Variadic flags with defaults should be exposed as arrays in the usage map
110107
cat <<'EOF' >mise.toml
111108
[tasks.usage-tera-var-flag-default]
112109
description = "Test variadic flags with default in usage map"
113110
usage = '''
111+
flag "--tag <tag>" help="Tag" var=#true {
112+
default {
113+
"foo"
114+
"bar"
115+
}
116+
}
117+
'''
118+
run = '''
119+
echo "tags={{ usage.tag | join(sep='::') }}"
120+
'''
121+
EOF
122+
123+
assert "mise run usage-tera-var-flag-default" "tags=foo::bar"
124+
125+
# Variadic flags with defaults should be exposed as arrays in the usage map, even if the default is a string
126+
cat <<'EOF' >mise.toml
127+
[tasks.usage-tera-var-flag-default-string]
128+
description = "Test variadic flags with default in usage map"
129+
usage = '''
114130
flag "--tag <tag>" help="Tag" var=#true default="foo bar"
115131
'''
116132
run = '''
117133
echo "tags={{ usage.tag | join(sep='::') }}"
118134
'''
119135
EOF
120-
# DISABLED: this is a bug in usage-lib.
121-
# Today, it treats the default as a string, not an array.
122-
# assert "mise run usage-tera-var-flag-default" "tags=foo::bar"
136+
137+
assert "mise run usage-tera-var-flag-default-string" "tags=foo bar"

src/task/task_script_parser.rs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,28 @@ impl TaskScriptParser {
152152
value.map(|v| Self::expect_array(v, param_name)).transpose()
153153
}
154154

155+
fn expect_opt_string_or_array(
156+
value: Option<&tera::Value>,
157+
param_name: &str,
158+
) -> tera::Result<Option<Vec<String>>> {
159+
value
160+
.map(|v| Self::expect_string_or_array(v, param_name))
161+
.transpose()
162+
}
163+
fn expect_string_or_array(value: &tera::Value, param_name: &str) -> tera::Result<Vec<String>> {
164+
match value {
165+
tera::Value::String(s) => Ok(vec![s.clone()]),
166+
tera::Value::Array(array) => Ok(array
167+
.iter()
168+
.map(|v| Self::expect_string(v, param_name))
169+
.collect::<Result<Vec<String>, tera::Error>>()?),
170+
_ => Err(tera::Error::msg(format!(
171+
"expected string or array for '{}', got {:?}",
172+
param_name, value
173+
))),
174+
}
175+
}
176+
155177
fn lock_error(e: impl std::fmt::Display) -> tera::Error {
156178
tera::Error::msg(format!("failed to lock: {}", e))
157179
}
@@ -207,7 +229,8 @@ impl TaskScriptParser {
207229

208230
let hide = Self::expect_opt_bool(args.get("hide"), "hide")?.unwrap_or(false);
209231

210-
let default = Self::expect_opt_string(args.get("default"), "default")?;
232+
let default = Self::expect_opt_string_or_array(args.get("default"), "default")?
233+
.unwrap_or_default();
211234

212235
let choices = Self::expect_opt_array(args.get("choices"), "choices")?
213236
.map(|array| {
@@ -278,7 +301,8 @@ impl TaskScriptParser {
278301
None => vec![name.clone()],
279302
};
280303

281-
let default = Self::expect_opt_string(args.get("default"), "default")?;
304+
let default = Self::expect_opt_string_or_array(args.get("default"), "default")?
305+
.unwrap_or_default();
282306

283307
let var = Self::expect_opt_bool(args.get("var"), "var")?.unwrap_or(false);
284308

@@ -384,7 +408,8 @@ impl TaskScriptParser {
384408
None => vec![name.clone()],
385409
};
386410

387-
let default = Self::expect_opt_string(args.get("default"), "default")?;
411+
let default = Self::expect_opt_string_or_array(args.get("default"), "default")?
412+
.unwrap_or_default();
388413

389414
let var = Self::expect_opt_bool(args.get("var"), "var")?.unwrap_or(false);
390415

@@ -817,19 +842,17 @@ impl TaskScriptParser {
817842
for flag in &spec.cmd.flags {
818843
let name = flag.name.to_snake_case();
819844
let value = if flag.var {
820-
// FIXME: This part is a bug in usage-lib.
821-
// It should be an empty array, but usage treats it as a string.
822-
// Should be: `tera::Value::Array(Vec::new())`
823-
tera::Value::String(String::new())
845+
tera::Value::Array(Vec::new())
824846
} else if flag.count {
825847
// Count flags: represent as an array of bools
826848
tera::Value::Array(Vec::new())
827-
} else if let Some(default) = &flag.default {
849+
} else if !flag.default.is_empty() {
828850
// if it is not parseable as a boolean, treat it as a string
829-
default.parse::<bool>().map_or_else(
830-
|_| tera::Value::String(String::new()),
831-
|_| tera::Value::Bool(false),
832-
)
851+
if flag.default.iter().all(|s| s.parse::<bool>().is_ok()) {
852+
tera::Value::Bool(false)
853+
} else {
854+
tera::Value::String(String::new())
855+
}
833856
} else {
834857
tera::Value::Bool(false)
835858
};
@@ -1284,7 +1307,7 @@ mod tests {
12841307
.iter_mut()
12851308
.find(|f| f.name == "bar")
12861309
{
1287-
bar_flag.default = Some("false".to_string());
1310+
bar_flag.default = vec!["false".to_string()];
12881311
}
12891312
// Now referencing usage.bar should render successfully, resolving to the default
12901313
let parsed_scripts = parser

0 commit comments

Comments
 (0)