Skip to content

Commit 3b3f32e

Browse files
committed
All three SetHandler methods now use InvocationContext. InjectedDependency input replaced with Context.
1 parent 9582bf3 commit 3b3f32e

File tree

5 files changed

+217
-89
lines changed

5 files changed

+217
-89
lines changed

src/FSharp.SystemCommandLine/CommandBuilders.fs

Lines changed: 128 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ let private def<'T> = Unchecked.defaultof<'T>
1616
let castValueDescriptor (ivdInputs: IValueDescriptor list) (idx: int) =
1717
ivdInputs[idx] :?> IValueDescriptor<'InputType>
1818

19-
let getValue<'V> (handlerInputs: HandlerInput list) (ctx: IC) (idx: int) =
19+
/// Parses a HandlerInput value using the InvocationContext.
20+
let parseInput<'V> (handlerInputs: HandlerInput list) (ctx: IC) (idx: int) =
2021
match handlerInputs[idx].Source with
2122
| ParsedOption o -> ctx.ParseResult.GetValueForOption<'V>(o :?> Option<'V>)
2223
| ParsedArgument a -> ctx.ParseResult.GetValueForArgument<'V>(a :?> Argument<'V>)
23-
| InjectedDependency -> ctx |> unbox<'V>
24+
| Context -> ctx |> unbox<'V>
2425

2526
type CommandSpec<'Inputs, 'Output> =
2627
{
@@ -117,48 +118,85 @@ type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
117118
match input.Source with
118119
| ParsedOption o -> cmd.AddOption o
119120
| ParsedArgument a -> cmd.AddArgument a
120-
| InjectedDependency -> ()
121+
| Context -> ()
121122
)
122123

123124
spec.SubCommands |> List.iter cmd.AddCommand
124125
cmd
125126

126127
/// Sets a command handler that returns `unit`.
127128
member this.SetHandlerUnit (spec: CommandSpec<'Inputs, unit>) (cmd: Command) =
128-
let handler (args: obj) = spec.Handler (args :?> 'Inputs)
129-
130-
let valueDescriptors =
131-
spec.Inputs
132-
|> List.choose (fun input ->
133-
match input.Source with
134-
| ParsedOption o -> o :> IValueDescriptor |> Some
135-
| ParsedArgument a -> a :> IValueDescriptor |> Some
136-
| InjectedDependency -> None
137-
)
129+
let handler (args: obj) =
130+
spec.Handler (args :?> 'Inputs)
138131

139-
/// Casts an IValueDescriptor to an IValueDescriptor<'T>
140-
let cvd idx = castValueDescriptor valueDescriptors idx
132+
let getValue (ctx: IC) (idx: int) =
133+
parseInput spec.Inputs ctx idx
141134

142135
match spec.Inputs.Length with
143-
| 00 -> cmd.SetHandler(Action(fun () -> handler ()))
144-
| 01 -> cmd.SetHandler(Action<'A>(fun a -> handler (a)), cvd 0)
145-
| 02 -> cmd.SetHandler(Action<'A, 'B>(fun a b -> handler (a, b)), cvd 0, cvd 1)
146-
| 03 -> cmd.SetHandler(Action<'A, 'B, 'C>(fun a b c -> handler (a, b, c)), cvd 0, cvd 1, cvd 2)
147-
| 04 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D>(fun a b c d -> handler (a, b, c, d)), cvd 0, cvd 1, cvd 2, cvd 3)
148-
| 05 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D, 'E>(fun a b c d e -> handler (a, b, c, d, e)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4)
149-
| 06 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D, 'E, 'F>(fun a b c d e f -> handler (a, b, c, d, e, f)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4, cvd 5)
150-
| 07 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D, 'E, 'F, 'G>(fun a b c d e f g -> handler (a, b, c, d, e, f, g)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4, cvd 5, cvd 6)
151-
| 08 -> cmd.SetHandler(Action<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H>(fun a b c d e f g h -> handler (a, b, c, d, e, f, g, h)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4, cvd 5, cvd 6, cvd 7)
136+
| 00 -> cmd.SetHandler(Action(fun () ->
137+
handler ()))
138+
| 01 -> cmd.SetHandler(Action<IC>(fun ctx ->
139+
let a: 'A = getValue ctx 0
140+
handler (a)))
141+
| 02 -> cmd.SetHandler(Action<IC>(fun ctx ->
142+
let a: 'A = getValue ctx 0
143+
let b: 'B = getValue ctx 1
144+
handler (a, b)))
145+
| 03 -> cmd.SetHandler(Action<IC>(fun ctx ->
146+
let a: 'A = getValue ctx 0
147+
let b: 'B = getValue ctx 1
148+
let c: 'C = getValue ctx 2
149+
handler (a, b, c)))
150+
| 04 -> cmd.SetHandler(Action<IC>(fun ctx ->
151+
let a: 'A = getValue ctx 0
152+
let b: 'B = getValue ctx 1
153+
let c: 'C = getValue ctx 2
154+
let d: 'D = getValue ctx 3
155+
handler (a, b, c, d)))
156+
| 05 -> cmd.SetHandler(Action<IC>(fun ctx ->
157+
let a: 'A = getValue ctx 0
158+
let b: 'B = getValue ctx 1
159+
let c: 'C = getValue ctx 2
160+
let d: 'D = getValue ctx 3
161+
let e: 'E = getValue ctx 4
162+
handler (a, b, c, d, e)))
163+
| 06 -> cmd.SetHandler(Action<IC>(fun ctx ->
164+
let a: 'A = getValue ctx 0
165+
let b: 'B = getValue ctx 1
166+
let c: 'C = getValue ctx 2
167+
let d: 'D = getValue ctx 3
168+
let e: 'E = getValue ctx 4
169+
let f: 'F = getValue ctx 5
170+
handler (a, b, c, d, e, f)))
171+
| 07 -> cmd.SetHandler(Action<IC>(fun ctx ->
172+
let a: 'A = getValue ctx 0
173+
let b: 'B = getValue ctx 1
174+
let c: 'C = getValue ctx 2
175+
let d: 'D = getValue ctx 3
176+
let e: 'E = getValue ctx 4
177+
let f: 'F = getValue ctx 5
178+
let g: 'G = getValue ctx 6
179+
handler (a, b, c, d, e, f, g)))
180+
| 08 -> cmd.SetHandler(Action<IC>(fun ctx ->
181+
let a: 'A = getValue ctx 0
182+
let b: 'B = getValue ctx 1
183+
let c: 'C = getValue ctx 2
184+
let d: 'D = getValue ctx 3
185+
let e: 'E = getValue ctx 4
186+
let f: 'F = getValue ctx 5
187+
let g: 'G = getValue ctx 6
188+
let h: 'H = getValue ctx 7
189+
handler (a, b, c, d, e, f, g, h)))
152190
| _ -> raise (NotImplementedException("Only 8 inputs are supported."))
153191
cmd
154192

155193
/// Sets a command handler that returns an `int` status code.
156-
member this.SetFuncHandlerInt (spec: CommandSpec<'Inputs, int>) (cmd: Command) =
194+
member this.SetHandlerInt (spec: CommandSpec<'Inputs, int>) (cmd: Command) =
157195
let handler (args: obj) =
158196
spec.Handler (args :?> 'Inputs)
159197

160198
let getValue (ctx: IC) (idx: int) =
161-
getValue spec.Inputs ctx idx
199+
parseInput spec.Inputs ctx idx
162200

163201
match spec.Inputs.Length with
164202
| 00 -> cmd.SetHandler(Action<IC>(fun ctx ->
@@ -219,34 +257,70 @@ type BaseCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
219257
cmd
220258

221259
/// Sets a command handler that returns a `Task`.
222-
member this.SetFuncHandlerTask (spec: CommandSpec<'Inputs, Task<'ReturnValue>>) (cmd: Command) =
260+
member this.SetHandlerTask (spec: CommandSpec<'Inputs, Task<'ReturnValue>>) (cmd: Command) =
223261
let handler (args: obj) =
224262
task {
225263
return! spec.Handler (args :?> 'Inputs)
226264
}
227265

228-
let valueDescriptors =
229-
spec.Inputs
230-
|> List.choose (fun input ->
231-
match input.Source with
232-
| ParsedOption o -> o :> IValueDescriptor |> Some
233-
| ParsedArgument a -> a :> IValueDescriptor |> Some
234-
| InjectedDependency -> None
235-
)
236-
237-
/// Casts an IValueDescriptor to an IValueDescriptor<'T>
238-
let cvd idx = castValueDescriptor valueDescriptors idx
266+
let getValue (ctx: IC) (idx: int) =
267+
parseInput spec.Inputs ctx idx
239268

240269
match spec.Inputs.Length with
241-
| 00 -> cmd.SetHandler(Func<Task>(fun () -> handler ()))
242-
| 01 -> cmd.SetHandler(Func<'A, Task>(fun a -> handler (a)), cvd 0)
243-
| 02 -> cmd.SetHandler(Func<'A, 'B, Task>(fun a b -> handler (a, b)), cvd 0, cvd 1)
244-
| 03 -> cmd.SetHandler(Func<'A, 'B, 'C, Task>(fun a b c -> handler (a, b, c)), cvd 0, cvd 1, cvd 2)
245-
| 04 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, Task>(fun a b c d -> handler (a, b, c, d)), cvd 0, cvd 1, cvd 2, cvd 3)
246-
| 05 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, Task>(fun a b c d e -> handler (a, b, c, d, e)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4)
247-
| 06 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, Task>(fun a b c d e f -> handler (a, b, c, d, e, f)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4, cvd 5)
248-
| 07 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, Task>(fun a b c d e f g -> handler (a, b, c, d, e, f, g)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4, cvd 5, cvd 6)
249-
| 08 -> cmd.SetHandler(Func<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, Task>(fun a b c d e f g h -> handler (a, b, c, d, e, f, g, h)), cvd 0, cvd 1, cvd 2, cvd 3, cvd 4, cvd 5, cvd 6, cvd 7)
270+
| 00 -> cmd.SetHandler(Func<Task>(fun () ->
271+
handler ()))
272+
| 01 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
273+
let a: 'A = getValue ctx 0
274+
handler (a)))
275+
| 02 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
276+
let a: 'A = getValue ctx 0
277+
let b: 'B = getValue ctx 1
278+
handler (a, b)))
279+
| 03 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
280+
let a: 'A = getValue ctx 0
281+
let b: 'B = getValue ctx 1
282+
let c: 'C = getValue ctx 2
283+
handler (a, b, c)))
284+
| 04 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
285+
let a: 'A = getValue ctx 0
286+
let b: 'B = getValue ctx 1
287+
let c: 'C = getValue ctx 2
288+
let d: 'D = getValue ctx 3
289+
handler (a, b, c, d)))
290+
| 05 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
291+
let a: 'A = getValue ctx 0
292+
let b: 'B = getValue ctx 1
293+
let c: 'C = getValue ctx 2
294+
let d: 'D = getValue ctx 3
295+
let e: 'E = getValue ctx 4
296+
handler (a, b, c, d, e)))
297+
| 06 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
298+
let a: 'A = getValue ctx 0
299+
let b: 'B = getValue ctx 1
300+
let c: 'C = getValue ctx 2
301+
let d: 'D = getValue ctx 3
302+
let e: 'E = getValue ctx 4
303+
let f: 'F = getValue ctx 5
304+
handler (a, b, c, d, e, f)))
305+
| 07 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
306+
let a: 'A = getValue ctx 0
307+
let b: 'B = getValue ctx 1
308+
let c: 'C = getValue ctx 2
309+
let d: 'D = getValue ctx 3
310+
let e: 'E = getValue ctx 4
311+
let f: 'F = getValue ctx 5
312+
let g: 'G = getValue ctx 6
313+
handler (a, b, c, d, e, f, g)))
314+
| 08 -> cmd.SetHandler(Func<IC, Task>(fun ctx ->
315+
let a: 'A = getValue ctx 0
316+
let b: 'B = getValue ctx 1
317+
let c: 'C = getValue ctx 2
318+
let d: 'D = getValue ctx 3
319+
let e: 'E = getValue ctx 4
320+
let f: 'F = getValue ctx 5
321+
let g: 'G = getValue ctx 6
322+
let h: 'H = getValue ctx 7
323+
handler (a, b, c, d, e, f, g, h)))
250324
| _ -> raise (NotImplementedException("Only 8 inputs are supported."))
251325
cmd
252326

@@ -277,15 +351,15 @@ type RootCommandParserBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>() =
277351
member this.Run (spec: CommandSpec<'Inputs, int>) =
278352
this.CommandLineBuilder.Command
279353
|> this.SetGeneralProperties spec
280-
|> this.SetFuncHandlerInt spec
354+
|> this.SetHandlerInt spec
281355
|> ignore
282356
this.CommandLineBuilder.Build()
283357

284358
/// Executes a Command with a handler that returns a Task<unit> or Task<int>.
285359
member this.Run (spec: CommandSpec<'Inputs, Task<'ReturnValue>>) =
286360
this.CommandLineBuilder.Command
287361
|> this.SetGeneralProperties spec
288-
|> this.SetFuncHandlerTask spec
362+
|> this.SetHandlerTask spec
289363
|> ignore
290364
this.CommandLineBuilder.Build()
291365

@@ -316,15 +390,15 @@ type RootCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>(args: string ar
316390
member this.Run (spec: CommandSpec<'Inputs, int>) =
317391
this.CommandLineBuilder.Command
318392
|> this.SetGeneralProperties spec
319-
|> this.SetFuncHandlerInt spec
393+
|> this.SetHandlerInt spec
320394
|> ignore
321395
this.CommandLineBuilder.Build().Parse(args).Invoke()
322396

323397
/// Executes a Command with a handler that returns a Task<unit> or Task<int>.
324398
member this.Run (spec: CommandSpec<'Inputs, Task<'ReturnValue>>) =
325399
this.CommandLineBuilder.Command
326400
|> this.SetGeneralProperties spec
327-
|> this.SetFuncHandlerTask spec
401+
|> this.SetHandlerTask spec
328402
|> ignore
329403
this.CommandLineBuilder.Build().Parse(args).InvokeAsync()
330404

@@ -343,13 +417,13 @@ type CommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>(name: string) =
343417
member this.Run (spec: CommandSpec<'Inputs, int>) =
344418
Command(name)
345419
|> this.SetGeneralProperties spec
346-
|> this.SetFuncHandlerInt spec
420+
|> this.SetHandlerInt spec
347421

348422
/// Executes a Command with a handler that returns a Task<unit> or Task<int>.
349423
member this.Run (spec: CommandSpec<'Inputs, Task<'ReturnValue>>) =
350424
Command(name)
351425
|> this.SetGeneralProperties spec
352-
|> this.SetFuncHandlerTask spec
426+
|> this.SetHandlerTask spec
353427

354428

355429
/// Builds a `System.CommandLine.RootCommand` using computation expression syntax.
@@ -361,5 +435,5 @@ let rootCommand<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>(args: string array)=
361435
RootCommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>(args)
362436

363437
/// Builds a `System.CommandLine.Command` using computation expression syntax.
364-
let command<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'I, 'J, 'K, 'L, 'M, 'N, 'O, 'P, 'Output> (name: string) =
438+
let command<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output> (name: string) =
365439
CommandBuilder<'A, 'B, 'C, 'D, 'E, 'F, 'G, 'H, 'Output>(name)

src/FSharp.SystemCommandLine/Inputs.fs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module private MaybeParser =
1717
type HandlerInputSource =
1818
| ParsedOption of Option
1919
| ParsedArgument of Argument
20-
| InjectedDependency
20+
| Context
2121

2222
type HandlerInput(source: HandlerInputSource) =
2323
member this.Source = source
@@ -132,6 +132,6 @@ type Input =
132132
|> fun o -> o.SetDefaultValue(None); o
133133
|> HandlerInput.OfArgument
134134

135-
/// Creates an injected dependency input.
136-
static member InjectedDependency<'T>() =
137-
HandlerInput<'T>(InjectedDependency)
135+
/// Passes the `InvocationContext` to the handler.
136+
static member Context() =
137+
HandlerInput<Invocation.InvocationContext>(Context)

src/TestConsole/ProgramTask.fs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@
33
open FSharp.SystemCommandLine
44
open System.Threading
55
open System.Threading.Tasks
6+
open System.CommandLine.Invocation
67

7-
let app (cancel: CancellationToken, words: string array, separator: string) =
8+
let app (ctx: InvocationContext, words: string array, separator: string) =
89
task {
10+
let cancel = ctx.GetCancellationToken()
11+
912
for i in [1..20] do
1013
if cancel.IsCancellationRequested then
1114
printfn "Cancellation Requested!"
@@ -20,13 +23,13 @@ let app (cancel: CancellationToken, words: string array, separator: string) =
2023

2124
//[<EntryPoint>]
2225
let main argv =
23-
let cancel = Input.InjectedDependency()
26+
let ctx = Input.Context()
2427
let words = Input.Option(["--word"; "-w"], [||], "A list of words to be appended")
2528
let separator = Input.Option(["--separator"; "-s"], ", ", "A character that will separate the joined words.")
2629

2730
rootCommand argv {
2831
description "Appends words together"
29-
inputs (cancel, words, separator)
32+
inputs (ctx, words, separator)
3033
setHandler app
3134
}
3235
|> Async.AwaitTask

src/Tests/SimpleAppTest.fs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,32 +12,50 @@ let setup () = handlerCalled <- false
1212
let tearDown () = handlerCalled =! true
1313

1414
[<Test>]
15-
let ``01 --word Hello -w World -s *`` () =
15+
let ``01 --word Hello -w World -s * return unit`` () =
1616
testRootCommand "--word Hello -w World -s *" {
1717
description "Appends words together"
1818
inputs (
1919
Input.Option(["--word"; "-w"], Array.empty, "A list of words to be appended"),
20-
Input.Option(["--separator"; "-s"], ",", "A character that will separate the joined words.")
20+
Input.OptionMaybe(["--separator"; "-s"], "A character that will separate the joined words.")
2121
)
2222
setHandler (fun (words, separator) ->
2323
words =! [| "Hello"; "World" |]
24-
separator =! "*"
24+
separator =! Some "*"
2525
handlerCalled <- true
2626
)
2727
} |> ignore
2828

2929
[<Test>]
30-
let ``02 --word Hello -w World -s * with int return`` () =
31-
testRootCommand "--word Hello -w World -s *" {
30+
let ``02 --word Hello -w World return unit`` () =
31+
testRootCommand "--word Hello -w World" {
3232
description "Appends words together"
3333
inputs (
3434
Input.Option(["--word"; "-w"], Array.empty, "A list of words to be appended"),
35-
Input.Option(["--separator"; "-s"], ",", "A character that will separate the joined words.")
35+
Input.OptionMaybe(["--separator"; "-s"], "A character that will separate the joined words.")
3636
)
3737
setHandler (fun (words, separator) ->
3838
words =! [| "Hello"; "World" |]
39-
separator =! "*"
39+
separator =! None
4040
handlerCalled <- true
41-
0
4241
)
4342
} |> ignore
43+
44+
[<Test>]
45+
let ``03 --word Hello -w World -s * return int`` () =
46+
let code =
47+
testRootCommand "--word Hello -w World -s *" {
48+
description "Appends words together"
49+
inputs (
50+
Input.Option(["--word"; "-w"], Array.empty, "A list of words to be appended"),
51+
Input.OptionMaybe(["--separator"; "-s"], "A character that will separate the joined words.")
52+
)
53+
setHandler (fun (words, separator) ->
54+
words =! [| "Hello"; "World" |]
55+
separator =! Some "*"
56+
handlerCalled <- true
57+
5
58+
)
59+
}
60+
61+
code =! 5

0 commit comments

Comments
 (0)