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 parameters #221

Merged
merged 5 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/Argu/Parsers/Cli.fs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +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>()

member val IsUsageRequested = false with get,set

Expand Down Expand Up @@ -121,13 +122,27 @@ type CliParseResultAggregator internal (argInfo : UnionArgInfo, stack : CliParse
if stack.TryDispatchResult result then ()
else unrecognizedParseResults.Add result.Value

member x.AppendResultWithNestedResults caseInfo context arguments nestedResults =
bartelink marked this conversation as resolved.
Show resolved Hide resolved
missingMandatoryCasesOfNestedResults.AddRange(nestedResults.MissingMandatoryCases)
x.AppendResult caseInfo context arguments

member _.AppendUnrecognized(token:string) = unrecognized.Add token

member x.ToUnionParseResults() =
{ Cases = results.Value |> Array.map (fun c -> c.ToArray()) ;
UnrecognizedCliParams = Seq.toList unrecognized ;
UnrecognizedCliParseResults = Seq.toList unrecognizedParseResults ;
IsUsageRequested = x.IsUsageRequested }
IsUsageRequested = x.IsUsageRequested
MissingMandatoryCases =
argInfo.Cases.Value
|> Seq.choose (fun case ->
if case.IsMandatory.Value && results.Value[case.Tag].Count = 0
then Some case
bartelink marked this conversation as resolved.
Show resolved Hide resolved
else None
)
|> Seq.append missingMandatoryCasesOfNestedResults
|> Seq.toList
}

// this rudimentary stack implementation assumes that only one subcommand
// can occur within any particular context; no need implement popping etc.
Expand Down Expand Up @@ -423,7 +438,7 @@ let rec private parseCommandLinePartial (state : CliParseState) (argInfo : Union
member _.Invoke<'Template when 'Template :> IArgParserTemplate> () =
new ParseResults<'Template>(nestedUnion, nestedResults, state.ProgramName, state.Description, state.UsageStringCharWidth, state.Exiter) :> obj }

aggregator.AppendResult caseInfo name [|result|]
aggregator.AppendResultWithNestedResults caseInfo name [|result|] nestedResults

| NullarySubCommand ->
aggregator.AppendResult caseInfo name [||]
Expand Down
24 changes: 6 additions & 18 deletions src/Argu/Parsers/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ let mkParseResultFromValues (info : UnionArgInfo) (exiter : IExiter) (width : in
UnrecognizedCliParams = []
UnrecognizedCliParseResults = []
Cases = agg |> Array.map (fun rs -> rs.ToArray())
MissingMandatoryCases = []
}

new ParseResults<'Template>(info, results, programName, description, width, exiter)
Expand All @@ -68,25 +69,11 @@ let postProcessResults (argInfo : UnionArgInfo) (ignoreMissingMandatory : bool)
| Choice1Of2 ts, ts' when caseInfo.GatherAllSources.Value -> Array.append ts ts'
| _, ts' -> ts'

let rec searchCaseInfoForError caseInfo =
match caseInfo.ParameterInfo.Value with
| SubCommand (_, unionArg, __) ->
match unionArg.Cases.Value with
| [| case |] ->
if case.IsMandatory.Value && not ignoreMissingMandatory then
Some (error unionArg ErrorCode.PostProcess "missing parameter '%s'." case.Name.Value)
else
searchCaseInfoForError case
| _ -> None
| _ -> None
match combined, commandLineResults with
| _, Some { MissingMandatoryCases = missingCase::_ } when not ignoreMissingMandatory ->
error argInfo ErrorCode.PostProcess "missing parameter '%s'." missingCase.Name.Value

match combined with
| [| sub |] ->
match searchCaseInfoForError sub.CaseInfo with
| Some error -> error
| _ -> combined

| [||] when caseInfo.IsMandatory.Value && not ignoreMissingMandatory ->
| [||], _ when caseInfo.IsMandatory.Value && not ignoreMissingMandatory ->
error argInfo ErrorCode.PostProcess "missing parameter '%s'." caseInfo.Name.Value
| _ -> combined

Expand All @@ -95,4 +82,5 @@ let postProcessResults (argInfo : UnionArgInfo) (ignoreMissingMandatory : bool)
UnrecognizedCliParams = match commandLineResults with Some clr -> clr.UnrecognizedCliParams | None -> []
UnrecognizedCliParseResults = match commandLineResults with Some clr -> clr.UnrecognizedCliParseResults | None -> []
IsUsageRequested = commandLineResults |> Option.exists (fun r -> r.IsUsageRequested)
MissingMandatoryCases = commandLineResults |> Option.map (fun c -> c.MissingMandatoryCases) |> Option.defaultValue []
}
1 change: 1 addition & 0 deletions src/Argu/UnionArgInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ type UnionParseResults =
UnrecognizedCliParseResults : obj list
/// Usage string requested by the caller
IsUsageRequested : bool
MissingMandatoryCases: UnionCaseArgInfo list
}

type UnionCaseArgInfo with
Expand Down
14 changes: 14 additions & 0 deletions tests/Argu.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,20 @@ module ``Argu Tests Main List`` =
raisesWith<ArguParseException> <@ parser.ParseCommandLine args @>
(fun e -> <@ e.FirstLine.Contains "--branch" @>)

[<Fact>]
let ``Main command parsing should not fail on missing mandatory sub command parameter if ignoreMissing`` () =
let args = [|"--mandatory-arg" ; "true" ; "checkout" |]
let results = parser.ParseCommandLine(args, ignoreMissing = true)
let nested = results.GetResult <@ Checkout @>
test <@ nested.Contains <@ Branch @> |> not @>
fpellet marked this conversation as resolved.
Show resolved Hide resolved

[<Fact>]
let ``Main command parsing should allow sub command if not missing mandatory parameter`` () =
let args = [|"--mandatory-arg" ; "true" ; "checkout"; "--branch"; "origin" |]
let results = parser.ParseCommandLine(args)
let nested = results.GetResult <@ Checkout @>
test <@ nested.GetResults <@ Branch @> = ["origin"] @>

[<Fact>]
let ``Main command parsing should fail on missing mandatory sub command's sub command parameter`` () =
let args = [|"--mandatory-arg"; "true"; "tag"; "--new"; |]
Expand Down