WoofWare.Whippet.Plugin.ArgParser
0.1.5
dotnet add package WoofWare.Whippet.Plugin.ArgParser --version 0.1.5
NuGet\Install-Package WoofWare.Whippet.Plugin.ArgParser -Version 0.1.5
<PackageReference Include="WoofWare.Whippet.Plugin.ArgParser" Version="0.1.5"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add WoofWare.Whippet.Plugin.ArgParser --version 0.1.5
#r "nuget: WoofWare.Whippet.Plugin.ArgParser, 0.1.5"
// Install WoofWare.Whippet.Plugin.ArgParser as a Cake Addin #addin nuget:?package=WoofWare.Whippet.Plugin.ArgParser&version=0.1.5 // Install WoofWare.Whippet.Plugin.ArgParser as a Cake Tool #tool nuget:?package=WoofWare.Whippet.Plugin.ArgParser&version=0.1.5
WoofWare.Whippet.Plugin.ArgParser
This is a Whippet plugin defining an argument parser.
It is a copy of the corresponding Myriad arg parser in WoofWare.Myriad, taken from commit d59ebdfccb87a06579fb99008a15f58ea8be394e.
Usage
Define an Args.fs
file like the following:
namespace MyNamespace
[<ArgParser>]
type LoadsOfTypes =
{
Foo : int
Bar : string
Baz : bool
SomeFile : FileInfo
SomeDirectory : DirectoryInfo
SomeList : DirectoryInfo list
OptionalThingWithNoDefault : int option
[<PositionalArgs>]
Positionals : int list
[<ArgumentDefaultFunction>]
OptionalThing : Choice<bool, bool>
[<ArgumentDefaultFunction>]
AnotherOptionalThing : Choice<int, int>
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
YetAnotherOptionalThing : Choice<string, string>
}
static member DefaultOptionalThing () = true
static member DefaultAnotherOptionalThing () = 3
In your fsproj:
<Project>
<ItemGroup>
<Compile Include="Args.fs" />
<Compile Include="GeneratedArgs.fs">
<WhippetFile>Args.fs</WhippetFile>
</Compile>
</ItemGroup>
<ItemGroup>
<PackageReference Include="WoofWare.Whippet.Plugin.ArgParser.Attributes" Version="" />
<PackageReference Include="WoofWare.Whippet.Plugin.ArgParser" WhippetPlugin="true" Version="" />
<PackageReference Include="WoofWare.Whippet" Version="" PrivateAssets="all" />
</ItemGroup>
</Project>
The generator will produce a file like the following:
[<RequireQualifiedAccess>]
module LoadsOfTypes =
// in case you want to test it, you get one with dependencies injected
let parse' (getEnvVar : string -> string) (args : string list) : LoadsOfTypes = ...
// this is the one we expect you actually want to use, if you don't want to test the arg parser
let parse (args : string list) : LoadsOfTypes = ...
Features
- Default arguments are handled as
Choice<'a, 'a>
: you get aChoice1Of2
if the user provided the input, or aChoice2Of2
if the parser filled in your specified default value. - Default arguments from the environment, specified with
[<ArgumentDefaultEnvironmentVariable "ENV_VAR">]
. If such an arg is not supplied on the command line, its value is parsed from the value of that env var. - Default arguments, specified with
[<ArgumentDefaultFunction>]
. If an arg[<ArgumentDefaultFunction>] Foo : Choice<'a, 'a>
is not supplied on the command line, the parser callsDefaultFoo : unit -> 'a
to obtain its value. - Positional arguments: a list with attribute
[<PositionalArgs>]
will accumulate all args which didn't match anything else. By default, the parser will fail if any of these arguments looks like an arg itself (i.e. it starts with--
) but comes before a positional arg separator--
; you can optionally give this attribute the argument(* includeFlagLike = *) true
to instead just put such flag-like args into the accumulator. - Positional args can also be of type
Choice<'a, 'a> list
, in which case we tell you whether the arg came before (Choice1Of2
) or after (Choice2Of2
) any--
positional args separator. - You can control TimeSpan and friends with the
[<InvariantCulture>]
and[<ParseExact @"hh\:mm\:ss">]
attributes. - By default, we generate F# extension methods for the type; you can instead create a module with the type's name, using
[<ArgParser (* isExtensionMethod = *) false>]
. - If
--help
appears in a position where the parser is expecting a key (e.g. in the first position, or after a--foo=bar
), the parser fails with help text. The parser also makes a limited effort to supply help text when encountering an invalid parse. - "Flag DUs": if a two-case DU appears in the generator input file, you can tag its cases as in
type DryRun = | [<ArgumentFlag false>] Wet | [<ArgumentFlag true>] Dry
. Then you can consume the flag like a bool:[<ArgParser>] type Args = { DryRun : DryRun }
, so--dry-run
is parsed intoDryRun.Dry
. - Control long forms of arguments with
[<ArgumentLongForm "alternative-name">] Foo : int
, so that instead of accepting the default--foo=3
, we accept--alternative-name=3
. - Custom help text for individual args is supplied with
[<ArgumentHelpText "this text is displayed next to the arg when the user calls --help">]
, and similarly help text for the entire args object is supplied with[<ArgParser>] [<ArgumentHelpText "hi!">] type Args = ...
.
Limitations
This is very bare-bones, but do raise GitHub issues if you like (or if you find cases where the parser does the wrong thing).
- Help is signalled by throwing an exception, so you'll get an unsightly stack trace and a nonzero exit code.
- Help doesn't take into account any arguments the user has entered. Ideally you'd get contextual information like an identification of which args the user has supplied at the point where the parse failed or help was requested.
- I don't handle very many types, and in particular a real arg parser would handle DUs and records with nesting.
- I don't try very hard to find a valid parse. It may well be possible to find a case where I fail to parse despite there existing a valid parse.
- There's no subcommand support (you'll have to do that yourself).
It should work fine if you just want to compose a few primitive types, though.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.1 is compatible. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.1
- FSharp.Core (>= 6.0.1)
- TypeEquality (>= 0.3.0)
- WoofWare.Whippet.Core (>= 0.1.2)
- WoofWare.Whippet.Fantomas (>= 0.3.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.