Skip to content

Commit

Permalink
subparser bugfix
Browse files Browse the repository at this point in the history
  • Loading branch information
eiriktsarpalis committed Jun 30, 2016
1 parent bfd2c23 commit 68690f8
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 11 deletions.
2 changes: 1 addition & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
### 3.0.0-alpha009
### 3.0.0-alpha010
* Add subcommand support.
* Add support for list and option parameters.
* Add support for grouped switches.
Expand Down
4 changes: 4 additions & 0 deletions src/Argu/ParseResult.fs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ type ParseResult<'Template when 'Template :> IArgParserTemplate>
/// accumulates if parsed with 'ignoreUnrecognized = true'
member __.UnrecognizedCliParams = results.UnrecognizedCliParams

/// Gets all parse results that are not part of the current parsing context
/// This is only applicable to subcommand parsing operations
member __.UnrecognizedCliParseResults = results.UnrecognizedCliParseResults

/// <summary>Query parse results for parameterless argument.</summary>
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
Expand Down
26 changes: 16 additions & 10 deletions src/Argu/Parsers/Cli.fs
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ type CliParseResultAggregator internal (argInfo : UnionArgInfo, stack : CliParse
let mutable resultCount = 0
let mutable isSubCommandDefined = false
let unrecognized = new ResizeArray<string>()
let unrecognizedParseResults = new ResizeArray<obj>()
let results = argInfo.Cases |> Array.map (fun _ -> new ResizeArray<UnionCaseParseResult> ())

member val IsUsageRequested = false with get,set
member __.ResultCount = resultCount
member __.IsSubCommandDefined = isSubCommandDefined

member __.AppendResult(result : UnionCaseParseResult) =
match result.CaseInfo.Depth with
| d when argInfo.Depth = d ->
if result.CaseInfo.Depth = argInfo.Depth then
resultCount <- resultCount + 1
let agg = results.[result.Tag]
if result.CaseInfo.IsUnique && agg.Count > 0 then
Expand All @@ -76,30 +76,36 @@ type CliParseResultAggregator internal (argInfo : UnionArgInfo, stack : CliParse
isSubCommandDefined <- true

agg.Add result

| d ->
else
// this parse result corresponds to an inherited parameter
// from a parent syntax. Use the ResultAggregator stack to
// re-route the result to its matching aggregator
stack.[d].AppendResult result
if stack.TryDispatchResult result then ()
else unrecognizedParseResults.Add result.Value

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

member __.ToUnionParseResults() =
{ Cases = results |> Array.map (fun c -> c.ToArray()) ;
UnrecognizedCliParams = Seq.toList unrecognized ;
UnrecognizedCliParseResults = Seq.toList unrecognizedParseResults ;
IsUsageRequested = __.IsUsageRequested }

// this rudimentary stack implementation assumes that only one subcommand
// can occur within any particular context; no need implement popping etc.
// Note that inheritting subcommands is explicitly prohibited by the library.
and CliParseResultAggregatorStack () =
let stack = new ResizeArray<CliParseResultAggregator>(capacity = 5)
and CliParseResultAggregatorStack (context : UnionArgInfo) =
let offset = context.Depth
let stack = new ResizeArray<CliParseResultAggregator>(capacity = 2)

member __.Item (depth : int) : CliParseResultAggregator = stack.[depth]
member self.TryDispatchResult(result : UnionCaseParseResult) =
if result.CaseInfo.Depth < offset then false
else
stack.[result.CaseInfo.Depth - offset].AppendResult result
true

member self.CreateNextAggregator(argInfo : UnionArgInfo) =
assert(stack.Count = argInfo.Depth)
assert(stack.Count = argInfo.Depth - offset)
let agg = new CliParseResultAggregator(argInfo, self)
stack.Add agg
agg
Expand Down Expand Up @@ -281,7 +287,7 @@ and parseCommandLine (argInfo : UnionArgInfo) (programName : string) (descriptio
let state = {
Reader = new CliTokenReader(inputs)
ProgramName = programName
ResultStack = new CliParseResultAggregatorStack()
ResultStack = new CliParseResultAggregatorStack(argInfo)
Description = description
UsageStringCharWidth = width
RaiseOnUsage = raiseOnUsage
Expand Down
2 changes: 2 additions & 0 deletions src/Argu/Parsers/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ let mkParseResultFromValues (info : UnionArgInfo) (exiter : IExiter) (width : in
{
IsUsageRequested = false
UnrecognizedCliParams = []
UnrecognizedCliParseResults = []
Cases = agg |> Array.map (fun rs -> rs.ToArray())
}

Expand Down Expand Up @@ -76,5 +77,6 @@ let postProcessResults (argInfo : UnionArgInfo) (ignoreMissingMandatory : bool)
{
Cases = argInfo.Cases |> Array.map combineSingle
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)
}
2 changes: 2 additions & 0 deletions src/Argu/UnionArgInfo.fs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ type UnionParseResults =
Cases : UnionCaseParseResult[][]
/// CLI tokens not recognized by the parser
UnrecognizedCliParams : string list
/// CLI parse objects not belonging to the current parser context
UnrecognizedCliParseResults : obj list
/// Usage string requested by the caller
IsUsageRequested : bool
}
Expand Down
6 changes: 6 additions & 0 deletions tests/Argu.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,12 @@ module ``Argu Tests`` =
test <@ match results.TryGetSubCommand() with Some (Clean _) -> true | _ -> false @>
test <@ nested.GetAllResults() = [F; D; X] @>

[<Fact>]
let ``SubParsers should correctly handle inherited params`` () =
let subParser = parser.GetSubCommandParser <@ Clean @>
let result = subParser.ParseCommandLine [|"-fdxv"|]
test <@ match result.UnrecognizedCliParseResults with [:? Argument as c ] -> c = Verbose | _ -> false @>

[<Fact>]
let ``Doubly nested subcommand parsing`` () =
let args = [|"required" ; "--foo" ; "sub" ; "-fdx" |]
Expand Down

0 comments on commit 68690f8

Please sign in to comment.