Skip to content

Commit

Permalink
v0.15.0-beta4 - Input configuration overloads
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanMarr committed Feb 25, 2023
1 parent 6880038 commit 7dff0ef
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<Version>0.14.0-beta4</Version>
<Version>0.15.0-beta4</Version>
<Description>F# computation expressions for working with the System.CommandLine API.</Description>
<Authors>Jordan Marr</Authors>
<PackageTags>F# fsharp System.CommandLine cli</PackageTags>
Expand Down
88 changes: 70 additions & 18 deletions src/FSharp.SystemCommandLine/Inputs.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -57,59 +87,81 @@ 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<bool>
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<bool>
Option<'T option>(
aliases |> Seq.toArray,
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."
),
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

Expand All @@ -118,7 +170,7 @@ type Input =
Argument<'T>(
name,
getDefaultValue = (fun () -> defaultValue),
description = (description |> Option.defaultValue null)
?description = description
)
|> HandlerInput.OfArgument

Expand All @@ -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
Expand Down
19 changes: 19 additions & 0 deletions src/Tests/SimpleAppTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,22 @@ let ``03 --word Hello -w World -s * return int`` () =
}

code =! 5

[<Test>]
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

0 comments on commit 7dff0ef

Please sign in to comment.