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

fix(Mandatory args): list missing cases #236

Merged
merged 18 commits into from
Mar 28, 2024
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 6.3.0
bartelink marked this conversation as resolved.
Show resolved Hide resolved
* Improve error message on missing cases on a subcommands (display all missing cases) [#236](https://github.com/fsprojects/Argu/pull/236)
fpellet marked this conversation as resolved.
Show resolved Hide resolved
* Fix the regression of the [#127](https://github.com/fsprojects/Argu/pull/127) merged in 6.1.2 and fix usage display when there are missing case in subcommands. [#236](https://github.com/fsprojects/Argu/pull/236)
fpellet marked this conversation as resolved.
Show resolved Hide resolved

### 6.2.2
* Fix default `programName` when invoking via a wrapper such as `dotnet.exe` [#233](https://github.com/fsprojects/Argu/pull/233)

Expand Down
8 changes: 6 additions & 2 deletions src/Argu/Parsers/Cli.fs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ type CliParseResultAggregator internal (argInfo : UnionArgInfo, stack : CliParse
let unrecognized = ResizeArray<string>()
let unrecognizedParseResults = ResizeArray<obj>()
let results = lazy(argInfo.Cases.Value |> Array.map (fun _ -> ResizeArray<UnionCaseParseResult>()))
let missingMandatoryCasesOfNestedResults = ResizeArray<UnionCaseArgInfo>()
let missingMandatoryCasesOfNestedResults = ResizeArray<UnionArgInfo * UnionCaseArgInfo list>()

member val IsUsageRequested = false with get,set

Expand Down Expand Up @@ -135,7 +135,11 @@ type CliParseResultAggregator internal (argInfo : UnionArgInfo, stack : CliParse
IsUsageRequested = x.IsUsageRequested
MissingMandatoryCases = [
yield! missingMandatoryCasesOfNestedResults
yield! argInfo.Cases.Value |> Seq.filter (fun case -> case.IsMandatory.Value && results.Value[case.Tag].Count = 0)

match argInfo.Cases.Value |> Seq.filter (fun case -> case.IsMandatory.Value && results.Value[case.Tag].Count = 0) |> Seq.toList with
| [] -> ()
| missingCases ->
yield argInfo, missingCases
]
}

Expand Down
5 changes: 3 additions & 2 deletions src/Argu/Parsers/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ let postProcessResults (argInfo : UnionArgInfo) (ignoreMissingMandatory : bool)
| _, ts' -> ts'

match combined, commandLineResults with
| _, Some { MissingMandatoryCases = missingCase::_ } when not ignoreMissingMandatory ->
error argInfo ErrorCode.PostProcess "missing parameter '%s'." missingCase.Name.Value
| _, Some { MissingMandatoryCases = (caseArgInfo, missingCases)::_ } when not ignoreMissingMandatory ->
let allCasesFormatted = missingCases |> Seq.map (fun c -> c.Name.Value) |> fun v -> System.String.Join("', '", v)
error caseArgInfo ErrorCode.PostProcess "missing parameter '%s'." allCasesFormatted

| [||], _ when caseInfo.IsMandatory.Value && not ignoreMissingMandatory ->
error argInfo ErrorCode.PostProcess "missing parameter '%s'." caseInfo.Name.Value
Expand Down
2 changes: 1 addition & 1 deletion src/Argu/UnionArgInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ type UnionParseResults =
UnrecognizedCliParseResults : obj list
/// Usage string requested by the caller
IsUsageRequested : bool
MissingMandatoryCases: UnionCaseArgInfo list
MissingMandatoryCases: (UnionArgInfo * UnionCaseArgInfo list) list
}

type UnionCaseArgInfo with
Expand Down
22 changes: 21 additions & 1 deletion tests/Argu.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ module ``Argu Tests Main List`` =
interface IArgParserTemplate with
member this.Usage = "gus"

type MultipleMandatoriesSubCommand =
| [<Mandatory>] ValueA of int
| [<Mandatory>] ValueB of int
| [<Mandatory>] ValueC of int
| ValueD of int
interface IArgParserTemplate with member this.Usage = "multiple mandatories subcommand arg"

type Argument =
| [<AltCommandLine("-v"); Inherit>] Verbose
| Working_Directory of string
Expand Down Expand Up @@ -116,6 +123,7 @@ module ``Argu Tests Main List`` =
| [<CliPrefix(CliPrefix.None)>] Clean of ParseResults<CleanArgs>
| [<CliPrefix(CliPrefix.None)>] Required of ParseResults<RequiredSubcommand>
| [<CliPrefix(CliPrefix.None)>] Unrecognized of ParseResults<GatherUnrecognizedSubcommand>
| [<CliPrefix(CliPrefix.None)>] Multiple_Mandatories of ParseResults<MultipleMandatoriesSubCommand>
| [<SubCommand; CliPrefix(CliPrefix.None)>] Nullary_Sub
interface IArgParserTemplate with
member a.Usage =
Expand Down Expand Up @@ -150,6 +158,7 @@ module ``Argu Tests Main List`` =
| Clean _ -> "clean state"
| Required _ -> "required subcommand"
| Unrecognized _ -> "unrecognized subcommand"
| Multiple_Mandatories _ -> "multiple mandatories subcommand"
| Nullary_Sub -> "nullary subcommand"
| List _ -> "variadic params"
| Optional _ -> "optional params"
Expand Down Expand Up @@ -474,6 +483,12 @@ module ``Argu Tests Main List`` =
raisesWith<ArguParseException> <@ parser.ParseCommandLine args @>
(fun e -> <@ e.FirstLine.Contains "--branch" @>)

[<Fact>]
let ``Main command parsing should fail and display all missing mandatories sub command parameters`` () =
let args = [|"--mandatory-arg" ; "true" ; "multiple-mandatories" ; "--valuea"; "5"|]
raisesWith<ArguParseException> <@ parser.ParseCommandLine(args, raiseOnUsage = true) @>
bartelink marked this conversation as resolved.
Show resolved Hide resolved
(fun e -> <@ e.FirstLine.Contains "ERROR: missing parameter '--valueb', '--valuec'." @>)

[<Fact>]
let ``Main command parsing should not fail on missing mandatory sub command parameter if ignoreMissing`` () =
let args = [|"--mandatory-arg" ; "true" ; "checkout" |]
Expand Down Expand Up @@ -661,7 +676,7 @@ module ``Argu Tests Main List`` =
[<Fact>]
let ``Get all subcommand parsers`` () =
let subcommands = parser.GetSubCommandParsers()
test <@ subcommands.Length = 6 @>
test <@ subcommands.Length = 7 @>
test <@ subcommands |> List.forall (fun sc -> sc.IsSubCommandParser) @>

[<Fact>]
Expand Down Expand Up @@ -852,6 +867,11 @@ module ``Argu Tests Main List`` =
let results = parser.ParseCommandLine (args, raiseOnUsage = false)
test <@ results.IsUsageRequested @>

[<Fact>]
let ``Required subcommand attribute should fail on missing subcommand and display usage of subcommand and not main command`` () =
bartelink marked this conversation as resolved.
Show resolved Hide resolved
let args = [|"--mandatory-arg" ; "true" ; "multiple-mandatories" ; "--valuea"; "5"|]
raisesWith<ArguParseException> <@ parser.ParseCommandLine(args) @>
(fun e -> <@ e.Message.Contains $"USAGE: {parser.ProgramName} multiple-mandatories [--help] --valuea <int>" @>)
bartelink marked this conversation as resolved.
Show resolved Hide resolved

[<HelpFlags("--my-help")>]
[<HelpDescription("waka jawaka")>]
Expand Down