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.RunSynchronously
No 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.RunSynchronously
Breaking Changes
setHandler
is deprecated in favor ofsetAction
to maintain parity with System.CommandLine v2.0.0.beta5 naming changes.Input
methods likeInput.Option
,Input.Argument
,Input.OptionMaybe
,Input.ArgumentMaybe
have 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.