From 7dff0ef16e3dcb1cd6d69dbd72d10c8650eb110a Mon Sep 17 00:00:00 2001 From: Jordan Marr Date: Sat, 25 Feb 2023 15:13:09 -0500 Subject: [PATCH] v0.15.0-beta4 - Input configuration overloads --- .../FSharp.SystemCommandLine.fsproj | 2 +- src/FSharp.SystemCommandLine/Inputs.fs | 88 +++++++++++++++---- src/Tests/SimpleAppTest.fs | 19 ++++ 3 files changed, 90 insertions(+), 19 deletions(-) diff --git a/src/FSharp.SystemCommandLine/FSharp.SystemCommandLine.fsproj b/src/FSharp.SystemCommandLine/FSharp.SystemCommandLine.fsproj index 109b195..776f350 100644 --- a/src/FSharp.SystemCommandLine/FSharp.SystemCommandLine.fsproj +++ b/src/FSharp.SystemCommandLine/FSharp.SystemCommandLine.fsproj @@ -3,7 +3,7 @@ net6.0 true - 0.14.0-beta4 + 0.15.0-beta4 F# computation expressions for working with the System.CommandLine API. Jordan Marr F# fsharp System.CommandLine cli diff --git a/src/FSharp.SystemCommandLine/Inputs.fs b/src/FSharp.SystemCommandLine/Inputs.fs index 7b6ef36..14564f6 100644 --- a/src/FSharp.SystemCommandLine/Inputs.fs +++ b/src/FSharp.SystemCommandLine/Inputs.fs @@ -24,31 +24,61 @@ type HandlerInput(source: HandlerInputSource) = type HandlerInput<'T>(inputType: HandlerInputSource) = inherit HandlerInput(inputType) + /// Converts a System.CommandLine.Option<'T> for usage with the CommandBuilder. static member OfOption<'T>(o: Option<'T>) = o :> Option |> ParsedOption |> HandlerInput<'T> + /// Converts a System.CommandLine.Argument<'T> for usage with the CommandBuilder. static member OfArgument<'T>(a: Argument<'T>) = a :> Argument |> ParsedArgument |> HandlerInput<'T> member this.GetValue(ctx: System.CommandLine.Invocation.InvocationContext) = match this.Source with | ParsedOption o -> o :?> Option<'T> |> ctx.ParseResult.GetValueForOption | ParsedArgument a -> a :?> Argument<'T> |> ctx.ParseResult.GetValueForArgument | Context -> ctx |> unbox<'T> - + +let private applyConfiguration configure a = + configure a; a /// Creates CLI options and arguments to be passed as command `inputs`. type Input = - + + /// Converts a System.CommandLine.Option<'T> for usage with the CommandBuilder. + static member OfOption<'T>(o: Option<'T>) = + HandlerInput.OfOption o + + /// Converts a System.CommandLine.Argument<'T> for usage with the CommandBuilder. + static member OfArgument<'T>(a: Argument<'T>) = + HandlerInput.OfArgument a + + /// Creates a CLI option of type 'T with the ability to manually configure the underlying properties. + static member Option<'T>(name: string, configure) = + Option<'T>(name) + |> applyConfiguration configure + |> HandlerInput.OfOption + + /// Creates a CLI option of type 'T with the ability to manually configure the underlying properties. + static member Option<'T>(aliases: string seq, configure) = + Option<'T>(Seq.toArray aliases) + |> applyConfiguration configure + |> HandlerInput.OfOption + + /// Creates a CLI argument of type 'T with the ability to manually configure the underlying properties. + static member Argument<'T>(name: string, configure) = + Argument<'T>(name) + |> applyConfiguration configure + |> HandlerInput.OfArgument + /// Creates a CLI option of type 'T. static member Option<'T>(name: string, ?description: string) = Option<'T>( name, - description = (description |> Option.defaultValue null) + ?description = description ) |> HandlerInput.OfOption /// Creates a CLI option of type 'T. static member Option<'T>(aliases: string seq, ?description: string) = Option<'T>( - aliases |> Seq.toArray, - description = (description |> Option.defaultValue null) + Seq.toArray aliases, + ?description = description ) |> HandlerInput.OfOption @@ -57,37 +87,59 @@ type Input = Option<'T>( name, getDefaultValue = (fun () -> defaultValue), - description = (description |> Option.defaultValue null) + ?description = description ) |> HandlerInput.OfOption /// Creates a CLI option of type 'T with a default value. static member Option<'T>(aliases: string seq, defaultValue: 'T, ?description: string) = Option<'T>( - aliases |> Seq.toArray, + Seq.toArray aliases, getDefaultValue = (fun () -> defaultValue), - description = (description |> Option.defaultValue null) + ?description = description ) |> HandlerInput.OfOption /// Creates a CLI option of type 'T that is required. static member OptionRequired<'T>(aliases: string seq, ?description: string) = Option<'T>( - aliases |> Seq.toArray, - description = (description |> Option.defaultValue null), + Seq.toArray aliases, + ?description = description, IsRequired = true ) |> HandlerInput.OfOption /// Creates a CLI option of type 'T that is required. static member OptionRequired<'T>(name: string, ?description: string) = - Input.OptionRequired<'T>([| name |], description |> Option.defaultValue null) + Input.OptionRequired<'T>([| name |], ?description = description) + + /// Creates a CLI option of type 'T option with the ability to manually configure the underlying properties. + static member OptionMaybe<'T>(aliases: string seq, configure) = + let isBool = typeof<'T> = typeof + Option<'T option>( + Seq.toArray aliases, + parseArgument = (fun argResult -> + match argResult.Tokens |> Seq.toList with + | [] when isBool -> true |> unbox<'T> |> Some + | [] -> None + | [ token ] -> MaybeParser.parseTokenValue token.Value + | _ :: _ -> failwith "F# Option can only be used with a single argument." + ), + Arity = ArgumentArity(0, 1) + ) + |> fun o -> o.SetDefaultValue(None); o + |> applyConfiguration configure + |> HandlerInput.OfOption + + /// Creates a CLI option of type 'T option with the ability to manually configure the underlying properties. + static member OptionMaybe<'T>(name: string, configure) = + Input.OptionMaybe<'T>([|name|], configure = configure) /// Creates a CLI option of type 'T option. static member OptionMaybe<'T>(aliases: string seq, ?description: string) = let isBool = typeof<'T> = typeof Option<'T option>( - aliases |> Seq.toArray, + Seq.toArray aliases, parseArgument = (fun argResult -> match argResult.Tokens |> Seq.toList with | [] when isBool -> true |> unbox<'T> |> Some @@ -95,21 +147,21 @@ type Input = | [ token ] -> MaybeParser.parseTokenValue token.Value | _ :: _ -> failwith "F# Option can only be used with a single argument." ), - description = (description |> Option.defaultValue null), + ?description = description, Arity = ArgumentArity(0, 1) ) |> fun o -> o.SetDefaultValue(None); o |> HandlerInput.OfOption /// Creates a CLI option of type 'T option. - static member OptionMaybe<'T>(name: string, ?description: string) = - Input.OptionMaybe<'T>([| name |], description |> Option.defaultValue null) + static member OptionMaybe<'T>(name: string, ?description) = + Input.OptionMaybe<'T>([| name |], ?description = description) /// Creates a CLI argument of type 'T. static member Argument<'T>(name: string, ?description: string) = Argument<'T>( name, - description = (description |> Option.defaultValue null) + ?description = description ) |> HandlerInput.OfArgument @@ -118,7 +170,7 @@ type Input = Argument<'T>( name, getDefaultValue = (fun () -> defaultValue), - description = (description |> Option.defaultValue null) + ?description = description ) |> HandlerInput.OfArgument @@ -132,7 +184,7 @@ type Input = | [ token ] -> MaybeParser.parseTokenValue token.Value | _ :: _ -> failwith "F# Option can only be used with a single argument." ), - description = (description |> Option.defaultValue null) + ?description = description ) |> fun o -> o.SetDefaultValue(None); o |> HandlerInput.OfArgument diff --git a/src/Tests/SimpleAppTest.fs b/src/Tests/SimpleAppTest.fs index 4ac1875..b167dce 100644 --- a/src/Tests/SimpleAppTest.fs +++ b/src/Tests/SimpleAppTest.fs @@ -59,3 +59,22 @@ let ``03 --word Hello -w World -s * return int`` () = } code =! 5 + +[] +let ``04 --word Hello -w World -s * return int using manual configured options`` () = + let code = + testRootCommand "--word Hello -w World -s *" { + description "Appends words together" + inputs ( + Input.Option(["--word"; "-w"], fun o -> o.SetDefaultValue Array.empty; o.Description <- "A list of words to be appended"), + Input.OptionMaybe(["--separator"; "-s"], fun o -> o.Description <- "A character that will separate the joined words.") + ) + setHandler (fun (words, separator) -> + words =! [| "Hello"; "World" |] + separator =! Some "*" + handlerCalled <- true + 5 + ) + } + + code =! 5