Skip to content

Commit bd53d14

Browse files
committed
Implemented a way to manually parse >8 inputs
1 parent 8f6f68b commit bd53d14

File tree

6 files changed

+75
-12
lines changed

6 files changed

+75
-12
lines changed

src/FSharp.SystemCommandLine/CommandBuilders.fs

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ open System.CommandLine
77
open System.CommandLine.Builder
88
open System.CommandLine.Parsing
99

10-
type private HI<'T> = HandlerInput<'T>
1110
type private IC = System.CommandLine.Invocation.InvocationContext
1211
let private def<'T> = Unchecked.defaultof<'T>
1312

@@ -24,11 +23,14 @@ type CommandSpec<'Inputs, 'Output> =
2423
Inputs: HandlerInput list
2524
Handler: 'Inputs -> 'Output
2625
SubCommands: System.CommandLine.Command list
26+
/// Registers extra inputs that can be parsed via the InvocationContext if more than 8 are required.
27+
ExtraInputs: HandlerInput list
2728
}
2829
static member Default =
2930
{
3031
Description = "My Command"
3132
Inputs = []
33+
ExtraInputs = []
3234
Handler = def<unit -> 'Output> // Support unit -> 'Output handler by default
3335
SubCommands = []
3436
}
@@ -40,6 +42,7 @@ type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
4042
{
4143
Description = spec.Description
4244
Inputs = spec.Inputs
45+
ExtraInputs = spec.ExtraInputs
4346
Handler = handler
4447
SubCommands = spec.SubCommands
4548
}
@@ -52,42 +55,52 @@ type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
5255
member this.Zero _ =
5356
CommandSpec<unit, 'Output>.Default
5457

58+
/// A description that will be displayed to the command line user.
5559
[<CustomOperation("description")>]
5660
member this.Description (spec: CommandSpec<'T, 'U>, description) =
5761
{ spec with Description = description }
5862

63+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
5964
[<CustomOperation("inputs")>]
60-
member this.Inputs (spec: CommandSpec<'T, 'Output>, a: HI<'A>) =
65+
member this.Inputs (spec: CommandSpec<'T, 'Output>, a: HandlerInput<'A>) =
6166
{ newHandler def<'A -> 'Output> spec with Inputs = [ a ] }
6267

68+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
6369
[<CustomOperation("inputs")>]
64-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>)) =
70+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>)) =
6571
{ newHandler def<'A * 'B -> 'Output> spec with Inputs = [ a; b ] }
6672

73+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
6774
[<CustomOperation("inputs")>]
68-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>, c: HI<'C>)) =
75+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>, c: HandlerInput<'C>)) =
6976
{ newHandler def<'A * 'B * 'C -> 'Output> spec with Inputs = [ a; b; c ] }
7077

78+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
7179
[<CustomOperation("inputs")>]
72-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>, c: HI<'C>, d: HI<'D>)) =
80+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>, c: HandlerInput<'C>, d: HandlerInput<'D>)) =
7381
{ newHandler def<'A * 'B * 'C * 'D -> 'Output> spec with Inputs = [ a; b; c; d ] }
7482

83+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
7584
[<CustomOperation("inputs")>]
76-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>, c: HI<'C>, d: HI<'D>, e: HI<'E>)) =
85+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>, c: HandlerInput<'C>, d: HandlerInput<'D>, e: HandlerInput<'E>)) =
7786
{ newHandler def<'A * 'B * 'C * 'D * 'E -> 'Output> spec with Inputs = [ a; b; c; d; e ] }
7887

88+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
7989
[<CustomOperation("inputs")>]
80-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>, c: HI<'C>, d: HI<'D>, e: HI<'E>, f: HI<'F>)) =
90+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>, c: HandlerInput<'C>, d: HandlerInput<'D>, e: HandlerInput<'E>, f: HandlerInput<'F>)) =
8191
{ newHandler def<'A * 'B * 'C * 'D * 'E * 'F -> 'Output> spec with Inputs = [ a; b; c; d; e; f ] }
8292

93+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
8394
[<CustomOperation("inputs")>]
84-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>, c: HI<'C>, d: HI<'D>, e: HI<'E>, f: HI<'F>, g: HI<'G>)) =
95+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>, c: HandlerInput<'C>, d: HandlerInput<'D>, e: HandlerInput<'E>, f: HandlerInput<'F>, g: HandlerInput<'G>)) =
8596
{ newHandler def<'A * 'B * 'C * 'D * 'E * 'F * 'G -> 'Output> spec with Inputs = [ a; b; c; d; e; f; g ] }
8697

98+
/// A tuple of inputs (max. 8) that must exactly match the handler function inputs.
8799
[<CustomOperation("inputs")>]
88-
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HI<'A>, b: HI<'B>, c: HI<'C>, d: HI<'D>, e: HI<'E>, f: HI<'F>, g: HI<'G>, h: HI<'H>)) =
100+
member this.Inputs (spec: CommandSpec<'T, 'Output>, (a: HandlerInput<'A>, b: HandlerInput<'B>, c: HandlerInput<'C>, d: HandlerInput<'D>, e: HandlerInput<'E>, f: HandlerInput<'F>, g: HandlerInput<'G>, h: HandlerInput<'H>)) =
89101
{ newHandler def<'A * 'B * 'C * 'D * 'E * 'F * 'G * 'H -> 'Output> spec with Inputs = [ a; b; c; d; e; f; g; h ] }
90102

103+
/// Sets a handler function that takes a tuple of inputs (max. 8). NOTE: This must be set after the inputs.
91104
[<CustomOperation("setHandler")>]
92105
member this.SetHandler (spec: CommandSpec<'Inputs, 'Output>, handler: 'Inputs -> 'Output) =
93106
newHandler handler spec
@@ -97,14 +110,21 @@ type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
97110
member this.SetCommand (spec: CommandSpec<'Inputs, 'Output>, subCommand: System.CommandLine.Command) =
98111
{ spec with SubCommands = spec.SubCommands @ [ subCommand ] }
99112

113+
/// Adds a sub-command.
100114
[<CustomOperation("addCommand")>]
101115
member this.AddCommand (spec: CommandSpec<'Inputs, 'Output>, subCommand: System.CommandLine.Command) =
102116
{ spec with SubCommands = spec.SubCommands @ [ subCommand ] }
103117

118+
/// Adds sub-commands.
104119
[<CustomOperation("addCommands")>]
105120
member this.AddCommands (spec: CommandSpec<'Inputs, 'Output>, subCommands: System.CommandLine.Command seq) =
106121
{ spec with SubCommands = spec.SubCommands @ (subCommands |> Seq.toList) }
107122

123+
/// Registers an additional input that can be manually parsed via the InvocationContext. (Use when more than 8 inputs are required.)
124+
[<CustomOperation("add")>]
125+
member this.Add(spec: CommandSpec<'Inputs, 'Output>, extraInput: HandlerInput<'Value>) =
126+
{ spec with ExtraInputs = spec.ExtraInputs @ [ extraInput ] }
127+
108128
/// Sets general properties on the command.
109129
member this.SetGeneralProperties (spec: CommandSpec<'T, 'U>) (cmd: Command) =
110130
cmd.Description <- spec.Description
@@ -115,6 +135,13 @@ type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
115135
| ParsedArgument a -> cmd.AddArgument a
116136
| Context -> ()
117137
)
138+
spec.ExtraInputs
139+
|> Seq.iter (fun input ->
140+
match input.Source with
141+
| ParsedOption o -> cmd.AddOption o
142+
| ParsedArgument a -> cmd.AddArgument a
143+
| Context -> ()
144+
)
118145

119146
spec.SubCommands |> List.iter cmd.AddCommand
120147
cmd

src/FSharp.SystemCommandLine/FSharp.SystemCommandLine.fsproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
<PropertyGroup>
44
<TargetFramework>net6.0</TargetFramework>
55
<GenerateDocumentationFile>true</GenerateDocumentationFile>
6-
<Version>0.13.0-beta4</Version>
6+
<Version>0.14.0-beta4</Version>
77
<Description>F# computation expressions for working with the System.CommandLine API.</Description>
88
<Authors>Jordan Marr</Authors>
9-
<PackageTags>F# fsharp System.CommandLine</PackageTags>
9+
<PackageTags>F# fsharp System.CommandLine cli</PackageTags>
1010
<PackageProjectUrl>https://github.com/JordanMarr/FSharp.SystemCommandLine</PackageProjectUrl>
1111
</PropertyGroup>
1212

src/FSharp.SystemCommandLine/Inputs.fs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ type HandlerInput<'T>(inputType: HandlerInputSource) =
2626
inherit HandlerInput(inputType)
2727
static member OfOption<'T>(o: Option<'T>) = o :> Option |> ParsedOption |> HandlerInput<'T>
2828
static member OfArgument<'T>(a: Argument<'T>) = a :> Argument |> ParsedArgument |> HandlerInput<'T>
29+
member this.GetValue(ctx: System.CommandLine.Invocation.InvocationContext) =
30+
match this.Source with
31+
| ParsedOption o -> o :?> Option<'T> |> ctx.ParseResult.GetValueForOption
32+
| ParsedArgument a -> a :?> Argument<'T> |> ctx.ParseResult.GetValueForArgument
33+
| Context -> ctx |> unbox<'T>
2934

3035

3136
/// Creates CLI options and arguments to be passed as command `inputs`.

src/TestConsole/Program.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ let app (words: string array, separator: string option) =
77
System.String.Join(separator, words) |> printfn "Result: %s"
88
0
99

10-
//[<EntryPoint>]
10+
[<EntryPoint>]
1111
let main argv =
1212
let words = Input.Option(["--word"; "-w"], Array.empty, "A list of words to be appended")
1313
let separator = Input.OptionMaybe(["--separator"; "-s"], "A character that will separate the joined words.")
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/// This option can be used when more than 8 inputs are required.
2+
module ProgramExtraInputs
3+
4+
open FSharp.SystemCommandLine
5+
6+
module Parameters =
7+
let words = Input.Option<string[]>(["--word"; "-w"], Array.empty, "A list of words to be appended")
8+
let separator = Input.OptionMaybe<string>(["--separator"; "-s"], "A character that will separate the joined words.")
9+
10+
let app (ctx: System.CommandLine.Invocation.InvocationContext) =
11+
// Manually parse parameters
12+
let words = Parameters.words.GetValue ctx
13+
let separator = Parameters.separator.GetValue ctx
14+
15+
// Append words together
16+
let separator = separator |> Option.defaultValue ", "
17+
System.String.Join(separator, words) |> printfn "Result: %s"
18+
0
19+
20+
//[<EntryPoint>]
21+
let main argv =
22+
let ctx = Input.Context()
23+
24+
rootCommand argv {
25+
description "Appends words together"
26+
inputs ctx
27+
setHandler app
28+
add Parameters.words
29+
add Parameters.separator
30+
}

src/TestConsole/TestConsole.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<Compile Include="ProgramSubCommand.fs" />
1313
<Compile Include="ProgramTask.fs" />
1414
<Compile Include="ProgramTokenReplacer.fs" />
15+
<Compile Include="ProgramExtraInputs.fs" />
1516
<Compile Include="Program.fs" />
1617
</ItemGroup>
1718

0 commit comments

Comments
 (0)