diff --git a/cli/usage.usage.kdl b/cli/usage.usage.kdl index 3103e54b1..7d4541327 100644 --- a/cli/usage.usage.kdl +++ b/cli/usage.usage.kdl @@ -31,7 +31,7 @@ cmd "complete-word" help="Generate shell completion candidates for a partial com flag "--cword" help="Current word index" { arg "" } - flag "--shell" required=#true default="bash" { + flag "--shell" default="bash" { arg "" { choices "bash" "fish" "nu" "powershell" "zsh" } @@ -70,7 +70,7 @@ cmd "generate" help="Generate completions, documentation, and other artifacts fr flag "--include-bash-completion-lib" help="Include https://github.com/scop/bash-completion" { long_help "Include https://github.com/scop/bash-completion\n\nThis is required for usage completions to work in bash, but the user may already provide it" } - flag "--usage-bin" help="Override the bin used for calling back to usage-cli" required=#true env="JDX_USAGE_BIN" default="usage" { + flag "--usage-bin" help="Override the bin used for calling back to usage-cli" env="JDX_USAGE_BIN" default="usage" { long_help "Override the bin used for calling back to usage-cli\n\nYou may need to set this if you have a different bin named \"usage\"" arg "" } @@ -88,7 +88,7 @@ cmd "generate" help="Generate completions, documentation, and other artifacts fr alias "init" hide=#true alias "completions-init" hide=#true long_help "Generate a shell init script that auto-completes any usage shebang script on $PATH\n\nSource the output once from your shell rc (e.g. ~/.bashrc) to enable\ntab-completion for any executable whose first line is a `usage` shebang —\nno per-script `usage g completion` step required." - flag "--usage-bin" help="Override the bin used for calling back to usage-cli" required=#true env="JDX_USAGE_BIN" default="usage" { + flag "--usage-bin" help="Override the bin used for calling back to usage-cli" env="JDX_USAGE_BIN" default="usage" { long_help "Override the bin used for calling back to usage-cli\n\nYou may need to set this if you have a different bin named \"usage\"" arg "" } @@ -158,7 +158,7 @@ cmd "generate" help="Generate completions, documentation, and other artifacts fr flag "-o --out-file" help="Output file path, or \"-\" for stdout (default)" effect="write" { arg "" } - flag "-s --section" help="Manual section number (default: 1)" required=#true default="1" { + flag "-s --section" help="Manual section number (default: 1)" default="1" { long_help "Manual section number (default: 1)\n\nCommon sections:\n- 1: User commands\n- 5: File formats\n- 7: Miscellaneous\n- 8: System administration commands" arg "
" } @@ -205,7 +205,7 @@ cmd "generate" help="Generate completions, documentation, and other artifacts fr } } cmd "lint" help="Lint a usage spec file for common issues" effect="read" { - flag "-f --format" help="Output format" required=#true default="text" { + flag "-f --format" help="Output format" default="text" { arg "" { choices "text" "json" } diff --git a/conformance/tests/derive.rs b/conformance/tests/derive.rs index 7123e8873..5b8128e60 100644 --- a/conformance/tests/derive.rs +++ b/conformance/tests/derive.rs @@ -423,7 +423,17 @@ fn a_defaulted_argument_reads_as_optional_in_both_renderers() { // `required=#false default=…`, so there is nothing to normalize. A *derived* one is // required by its type and defaulted by its attribute, which is exactly the shape that // diverges. - let spec: LibSpec = Defaulted::to_kdl().parse().expect("valid spec"); + let kdl = Defaulted::to_kdl(); + let derived = Defaulted::spec(); + assert!(!derived.root.args[0].required, "{kdl}"); + assert!(!derived.root.flags[0].required, "{kdl}"); + assert!(kdl.contains(r#"arg "[DIR]""#), "{kdl}"); + assert!( + !kdl.contains(r#"flag "--out " required=#true"#), + "{kdl}" + ); + + let spec: LibSpec = kdl.parse().expect("valid spec"); let theirs = format!("ex {}", spec.cmd.usage()).trim().to_string(); let ours = usage_argv::help::usage_line(&["ex"], Defaulted::spec().root); assert_eq!(ours, theirs, "the two renderers disagree"); diff --git a/derive/src/codegen.rs b/derive/src/codegen.rs index a567e5d30..4a2a8ce67 100644 --- a/derive/src/codegen.rs +++ b/derive/src/codegen.rs @@ -1071,7 +1071,8 @@ fn flag_meta(i: usize, field: &Field, owner: &syn::Ident) -> TokenStream { // describe a different CLI from the one that runs. // A collecting field's type cannot say whether one value is needed, so `required` may // declare it. Every other shape gets its answer from the type. - let required = field.shape == Shape::Required || field.required_collection; + let required = + (field.shape == Shape::Required || field.required_collection) && field.default.is_empty(); // Declared, not inferred: `Option` already says the *flag* is optional and says // nothing about whether its value is. let value_optional = field.value_optional; @@ -1168,7 +1169,8 @@ fn arg_meta(i: usize, field: &Field, owner: &syn::Ident) -> TokenStream { // `String` must be filled; `Option` and `Vec` need not be. // A collecting field's type cannot say whether one value is needed, so `required` may // declare it. Every other shape gets its answer from the type. - let required = field.shape == Shape::Required || field.required_collection; + let required = + (field.shape == Shape::Required || field.required_collection) && field.default.is_empty(); let (choices, accepted_choices, choice_aliases, ignore_case) = choices_tokens(field); let validate = option_str(field.validate.as_deref()); let validate_error = option_str(field.validate_error.as_deref());