v2.0.0-beta5
This release provides compatibility with the latest System.CommandLine v2.0.0-beta5 rework!
New Input API
The new Input API has been redesigned to be easier to use and more extensible.
View the Input API in the README for more details and extensibility examples.
Old:
let unzipFile = Input.Argument<FileInfo>("The file to unzip")
let outputDir = Input.OptionMaybe<DirectoryInfo>(["--output"; "-o"], "The output directory")New:
let unzipFile = argument<FileInfo> "unzipFile" |> desc "The file to unzip"
let outputDir = optionMaybe<DirectoryInfo> "--output" |> alias "-o" |> desc "The output directory"Improved Validation
The new Input.validate function allows you to return a validation Result against the parsed value.
(There is also an addValidator function that aligns more closely to the built-in System.CommandLine approach.)
open System.IO
open FSharp.SystemCommandLine
open Input
let unzip (zipFile: FileInfo, outputDirMaybe: DirectoryInfo option) =
// Default to the zip file dir if None
let outputDir = defaultArg outputDirMaybe zipFile.Directory
printfn $"Unzipping {zipFile.Name} to {outputDir.FullName}..."
[<EntryPoint>]
let main argv =
rootCommand argv {
description "Unzips a .zip file"
inputs (
argument "zipfile"
|> description "The file to unzip"
|> validateFileExists
|> validate (fun zipFile ->
if zipFile.Length > 500000
then Error $"File cannot be bigger than 500 KB"
else Ok ()
),
optionMaybe "--output"
|> alias "-o"
|> description "The output directory"
|> validateDirectoryExists
)
setAction unzip
}Showing Help by Default in Root Command
For apps that utilize sub-commands, a common pattern is to display help output when no sub-command is entered or do nothing.
To show help, you can replace setAction with one of two new custom operations: helpAction or helpActionAsync.
NOTE that you must include inputs Input.context.
Non-async:
[<EntryPoint>]
let main argv =
rootCommand argv {
description "File System Manager"
inputs Input.context
helpAction
addCommands [ listCmd; deleteCmd ]
}Async:
[<EntryPoint>]
let main argv =
rootCommand argv {
description "File System Manager"
inputs Input.context
helpActionAsync
addCommands [ listCmd; deleteCmd ]
}
|> Async.AwaitTask
|> Async.RunSynchronouslyNo Action in Root Command
Another common use case for apps that use only sub-commands is to not provide any action (handler) function for the rootCommand.
Previously, you would do this by setting the action as setAction id or setAction Task.FromResult for async apps.
Now you can express these as: noAction or noActionAsync for async apps.
Non-async:
[<EntryPoint>]
let main argv =
rootCommand argv {
description "File System Manager"
noAction
addCommands [ listCmd; deleteCmd ]
}Async app:
[<EntryPoint>]
let main argv =
rootCommand argv {
description "File System Manager"
noActionAsync
addCommands [ listCmd; deleteCmd ]
}
|> Async.AwaitTask
|> Async.RunSynchronouslyBreaking Changes
setHandleris deprecated in favor ofsetActionto maintain parity with System.CommandLine v2.0.0.beta5 naming changes.Inputmethods likeInput.Option,Input.Argument,Input.OptionMaybe,Input.ArgumentMaybehave been replaced with pipelined functions to avoid "overload soup" and to make it easier and more extensible for users to create their own custom functions against the underlying System.CommandLine API.