Skip to content

Commit

Permalink
fix(Mandatory args error): Show missing cases (#236)
Browse files Browse the repository at this point in the history
  • Loading branch information
fpellet authored Mar 28, 2024
1 parent 5fb339f commit e3bc012
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 6 deletions.
4 changes: 4 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### 6.2.3
* Improve error message on missing cases on a subcommands (display all missing cases) [#236](https://github.com/fsprojects/Argu/pull/236) [@fpellet](https://github.com/fpellet)
* 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](https://github.com/fpellet)

### 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
23 changes: 22 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 @>
(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,12 @@ module ``Argu Tests Main List`` =
let results = parser.ParseCommandLine (args, raiseOnUsage = false)
test <@ results.IsUsageRequested @>

[<Fact>]
let ``Should fail if mandatory case is missing on a subcommand and display usage of subcommand and not main command`` () =
let args = [|"--mandatory-arg" ; "true" ; "multiple-mandatories" ; "--valuea"; "5"|]
raisesWith<ArguParseException> <@ parser.ParseCommandLine args @>
(fun e -> <@ e.FirstLine.Contains "ERROR: missing parameter '--valueb', '--valuec'"
&& e.Message.Contains $"USAGE: {parser.ProgramName} multiple-mandatories [--help] --valuea <int>" @>)

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

0 comments on commit e3bc012

Please sign in to comment.