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
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)>()
fpellet marked this conversation as resolved.
Show resolved Hide resolved

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
fpellet marked this conversation as resolved.
Show resolved Hide resolved
}

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

type SeveralMandatoriesSubCommand =
fpellet marked this conversation as resolved.
Show resolved Hide resolved
| [<Mandatory>] ValueA of int
| [<Mandatory>] ValueB of int
| [<Mandatory>] ValueC of int
| ValueD of int
with
bartelink marked this conversation as resolved.
Show resolved Hide resolved
interface IArgParserTemplate with
bartelink marked this conversation as resolved.
Show resolved Hide resolved
member this.Usage =
match this with
| ValueA _ -> "Value a"
| ValueB _ -> "Value b"
| ValueC _ -> "Value c"
| ValueD _ -> "Value d"

type Argument =
| [<AltCommandLine("-v"); Inherit>] Verbose
| Working_Directory of string
Expand Down Expand Up @@ -116,6 +130,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)>] Several_Mandatories of ParseResults<SeveralMandatoriesSubCommand>
| [<SubCommand; CliPrefix(CliPrefix.None)>] Nullary_Sub
interface IArgParserTemplate with
member a.Usage =
Expand Down Expand Up @@ -150,6 +165,7 @@ module ``Argu Tests Main List`` =
| Clean _ -> "clean state"
| Required _ -> "required subcommand"
| Unrecognized _ -> "unrecognized subcommand"
| Several_Mandatories _ -> "several mandatories subcommand"
| Nullary_Sub -> "nullary subcommand"
| List _ -> "variadic params"
| Optional _ -> "optional params"
Expand Down Expand Up @@ -474,6 +490,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" ; "several-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 +683,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 +874,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 subcommand2`` () =
bartelink marked this conversation as resolved.
Show resolved Hide resolved
let args = [|"--mandatory-arg" ; "true" ; "several-mandatories" ; "--valuea"; "5"|]
raisesWith<ArguParseException> <@ parser.ParseCommandLine(args, raiseOnUsage = true) @>
(fun e -> <@ e.Message.Contains "valuec" && e.Message.Contains "valuea" @>)
bartelink marked this conversation as resolved.
Show resolved Hide resolved

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