mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-12-14 21:05:39 +00:00
Implements support for --no- prefix negation on boolean and flag DU fields when marked with [<ArgumentNegateWithPrefix>]. This allows both --flag and --no-flag forms to be accepted, with --no- variants negating the value. Changes: - Extend ArgParserGenerator to generate --no- prefix handling - Add conflict detection for overlapping --no- prefixed arguments - Update help text to display both forms (e.g., --verbose / --no-verbose) - Add test examples in ArgParserNegationTests.fs demonstrating: - Boolean field negation - Flag DU negation - Multiple ArgumentLongForm with negation - Combined features (defaults, help text) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1030 lines
46 KiB
Forth
1030 lines
46 KiB
Forth
//------------------------------------------------------------------------------
|
|
// This code was generated by myriad.
|
|
// Changes to this file will be lost when the code is regenerated.
|
|
//------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace ConsumePlugin
|
|
|
|
open WoofWare.Myriad.Plugins
|
|
|
|
/// Methods to parse arguments for the type BoolNegation
|
|
[<AutoOpen>]
|
|
module BoolNegationArgParse =
|
|
type private ParseState_BoolNegation =
|
|
/// Ready to consume a key or positional arg
|
|
| AwaitingKey
|
|
/// Waiting to receive a value for the key we've already consumed
|
|
| AwaitingValue of key : string
|
|
|
|
/// Extension methods for argument parsing
|
|
type BoolNegation with
|
|
|
|
static member parse' (getEnvironmentVariable : string -> string option) (args : string list) : BoolNegation =
|
|
let ArgParser_errors = ResizeArray ()
|
|
|
|
let helpText () =
|
|
[
|
|
(sprintf "%s bool%s%s" (sprintf "--%s / --no-%s" "enable-feature" "enable-feature") "" "")
|
|
]
|
|
|> String.concat "\n"
|
|
|
|
let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
|
|
let mutable arg_0 : bool option = None
|
|
|
|
/// Processes the key-value pair, returning Error if no key was matched.
|
|
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
|
|
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
|
|
let processKeyValue (key : string) (value : string) : Result<unit, string option> =
|
|
if
|
|
System.String.Equals (
|
|
key,
|
|
sprintf "--no-%s" "enable-feature",
|
|
System.StringComparison.OrdinalIgnoreCase
|
|
)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "enable-feature" "enable-feature")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> not (System.Boolean.Parse x)) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (
|
|
key,
|
|
sprintf "--%s" "enable-feature",
|
|
System.StringComparison.OrdinalIgnoreCase
|
|
)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "enable-feature" "enable-feature")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> System.Boolean.Parse x) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else
|
|
Error None
|
|
|
|
/// Returns false if we didn't set a value.
|
|
let setFlagValue (key : string) : bool =
|
|
if
|
|
System.String.Equals (
|
|
key,
|
|
sprintf "--no-%s" "enable-feature",
|
|
System.StringComparison.OrdinalIgnoreCase
|
|
)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Flag '%s' was supplied multiple times"
|
|
(sprintf "--%s / --no-%s" "enable-feature" "enable-feature")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- false |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (
|
|
key,
|
|
sprintf "--%s" "enable-feature",
|
|
System.StringComparison.OrdinalIgnoreCase
|
|
)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Flag '%s' was supplied multiple times"
|
|
(sprintf "--%s / --no-%s" "enable-feature" "enable-feature")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- true |> Some
|
|
true
|
|
else
|
|
false
|
|
|
|
let rec go (state : ParseState_BoolNegation) (args : string list) =
|
|
match args with
|
|
| [] ->
|
|
match state with
|
|
| ParseState_BoolNegation.AwaitingKey -> ()
|
|
| ParseState_BoolNegation.AwaitingValue key ->
|
|
if setFlagValue key then
|
|
()
|
|
else
|
|
sprintf
|
|
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
|
|
key
|
|
|> ArgParser_errors.Add
|
|
| "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
|
|
| arg :: args ->
|
|
match state with
|
|
| ParseState_BoolNegation.AwaitingKey ->
|
|
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
|
|
if arg = "--help" then
|
|
helpText () |> failwithf "Help text requested.\n%s"
|
|
else
|
|
let equals = arg.IndexOf (char 61)
|
|
|
|
if equals < 0 then
|
|
args |> go (ParseState_BoolNegation.AwaitingValue arg)
|
|
else
|
|
let key = arg.[0 .. equals - 1]
|
|
let value = arg.[equals + 1 ..]
|
|
|
|
match processKeyValue key value with
|
|
| Ok () -> go ParseState_BoolNegation.AwaitingKey args
|
|
| Error x ->
|
|
match x with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process argument %s as key %s and value %s"
|
|
arg
|
|
key
|
|
value
|
|
| Some msg ->
|
|
sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
|
|
go ParseState_BoolNegation.AwaitingKey args
|
|
else
|
|
arg |> (fun x -> x) |> parser_LeftoverArgs.Add
|
|
go ParseState_BoolNegation.AwaitingKey args
|
|
| ParseState_BoolNegation.AwaitingValue key ->
|
|
match processKeyValue key arg with
|
|
| Ok () -> go ParseState_BoolNegation.AwaitingKey args
|
|
| Error exc ->
|
|
if setFlagValue key then
|
|
go ParseState_BoolNegation.AwaitingKey (arg :: args)
|
|
else
|
|
match exc with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process supplied arg %s. Help text follows.\n%s"
|
|
key
|
|
(helpText ())
|
|
| Some msg -> msg |> ArgParser_errors.Add
|
|
|
|
go ParseState_BoolNegation.AwaitingKey args
|
|
|
|
let parser_LeftoverArgs =
|
|
if 0 = parser_LeftoverArgs.Count then
|
|
()
|
|
else
|
|
parser_LeftoverArgs
|
|
|> String.concat " "
|
|
|> sprintf "There were leftover args: %s"
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
|
|
let arg_0 =
|
|
match arg_0 with
|
|
| None ->
|
|
sprintf
|
|
"Required argument '%s' received no value"
|
|
(sprintf "--%s / --no-%s" "enable-feature" "enable-feature")
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
| Some x -> x
|
|
|
|
if 0 = ArgParser_errors.Count then
|
|
{
|
|
EnableFeature = arg_0
|
|
}
|
|
else
|
|
ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
|
|
|
|
static member parse (args : string list) : BoolNegation =
|
|
BoolNegation.parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args
|
|
namespace ConsumePlugin
|
|
|
|
open WoofWare.Myriad.Plugins
|
|
|
|
/// Methods to parse arguments for the type FlagNegation
|
|
[<AutoOpen>]
|
|
module FlagNegationArgParse =
|
|
type private ParseState_FlagNegation =
|
|
/// Ready to consume a key or positional arg
|
|
| AwaitingKey
|
|
/// Waiting to receive a value for the key we've already consumed
|
|
| AwaitingValue of key : string
|
|
|
|
/// Extension methods for argument parsing
|
|
type FlagNegation with
|
|
|
|
static member parse' (getEnvironmentVariable : string -> string option) (args : string list) : FlagNegation =
|
|
let ArgParser_errors = ResizeArray ()
|
|
|
|
let helpText () =
|
|
[
|
|
(sprintf "%s bool%s%s" (sprintf "--%s / --no-%s" "dry-run" "dry-run") "" "")
|
|
]
|
|
|> String.concat "\n"
|
|
|
|
let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
|
|
let mutable arg_0 : TestDryRunMode option = None
|
|
|
|
/// Processes the key-value pair, returning Error if no key was matched.
|
|
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
|
|
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
|
|
let processKeyValue (key : string) (value : string) : Result<unit, string option> =
|
|
if
|
|
System.String.Equals (key, sprintf "--no-%s" "dry-run", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "dry-run" "dry-run")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <-
|
|
value
|
|
|> (fun x ->
|
|
if not (System.Boolean.Parse x) = false then
|
|
TestDryRunMode.Wet
|
|
else
|
|
TestDryRunMode.Dry
|
|
)
|
|
|> Some
|
|
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "dry-run" "dry-run")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <-
|
|
value
|
|
|> (fun x ->
|
|
if System.Boolean.Parse x = false then
|
|
TestDryRunMode.Wet
|
|
else
|
|
TestDryRunMode.Dry
|
|
)
|
|
|> Some
|
|
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else
|
|
Error None
|
|
|
|
/// Returns false if we didn't set a value.
|
|
let setFlagValue (key : string) : bool =
|
|
if
|
|
System.String.Equals (key, sprintf "--no-%s" "dry-run", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s / --no-%s" "dry-run" "dry-run")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- TestDryRunMode.Wet |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "dry-run", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s / --no-%s" "dry-run" "dry-run")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <-
|
|
if true = false then
|
|
TestDryRunMode.Wet
|
|
else
|
|
TestDryRunMode.Dry
|
|
|> Some
|
|
|
|
true
|
|
else
|
|
false
|
|
|
|
let rec go (state : ParseState_FlagNegation) (args : string list) =
|
|
match args with
|
|
| [] ->
|
|
match state with
|
|
| ParseState_FlagNegation.AwaitingKey -> ()
|
|
| ParseState_FlagNegation.AwaitingValue key ->
|
|
if setFlagValue key then
|
|
()
|
|
else
|
|
sprintf
|
|
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
|
|
key
|
|
|> ArgParser_errors.Add
|
|
| "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
|
|
| arg :: args ->
|
|
match state with
|
|
| ParseState_FlagNegation.AwaitingKey ->
|
|
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
|
|
if arg = "--help" then
|
|
helpText () |> failwithf "Help text requested.\n%s"
|
|
else
|
|
let equals = arg.IndexOf (char 61)
|
|
|
|
if equals < 0 then
|
|
args |> go (ParseState_FlagNegation.AwaitingValue arg)
|
|
else
|
|
let key = arg.[0 .. equals - 1]
|
|
let value = arg.[equals + 1 ..]
|
|
|
|
match processKeyValue key value with
|
|
| Ok () -> go ParseState_FlagNegation.AwaitingKey args
|
|
| Error x ->
|
|
match x with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process argument %s as key %s and value %s"
|
|
arg
|
|
key
|
|
value
|
|
| Some msg ->
|
|
sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
|
|
go ParseState_FlagNegation.AwaitingKey args
|
|
else
|
|
arg |> (fun x -> x) |> parser_LeftoverArgs.Add
|
|
go ParseState_FlagNegation.AwaitingKey args
|
|
| ParseState_FlagNegation.AwaitingValue key ->
|
|
match processKeyValue key arg with
|
|
| Ok () -> go ParseState_FlagNegation.AwaitingKey args
|
|
| Error exc ->
|
|
if setFlagValue key then
|
|
go ParseState_FlagNegation.AwaitingKey (arg :: args)
|
|
else
|
|
match exc with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process supplied arg %s. Help text follows.\n%s"
|
|
key
|
|
(helpText ())
|
|
| Some msg -> msg |> ArgParser_errors.Add
|
|
|
|
go ParseState_FlagNegation.AwaitingKey args
|
|
|
|
let parser_LeftoverArgs =
|
|
if 0 = parser_LeftoverArgs.Count then
|
|
()
|
|
else
|
|
parser_LeftoverArgs
|
|
|> String.concat " "
|
|
|> sprintf "There were leftover args: %s"
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
|
|
let arg_0 =
|
|
match arg_0 with
|
|
| None ->
|
|
sprintf "Required argument '%s' received no value" (sprintf "--%s / --no-%s" "dry-run" "dry-run")
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
| Some x -> x
|
|
|
|
if 0 = ArgParser_errors.Count then
|
|
{
|
|
DryRun = arg_0
|
|
}
|
|
else
|
|
ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
|
|
|
|
static member parse (args : string list) : FlagNegation =
|
|
FlagNegation.parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args
|
|
namespace ConsumePlugin
|
|
|
|
open WoofWare.Myriad.Plugins
|
|
|
|
/// Methods to parse arguments for the type MultipleFormsNegation
|
|
[<AutoOpen>]
|
|
module MultipleFormsNegationArgParse =
|
|
type private ParseState_MultipleFormsNegation =
|
|
/// Ready to consume a key or positional arg
|
|
| AwaitingKey
|
|
/// Waiting to receive a value for the key we've already consumed
|
|
| AwaitingValue of key : string
|
|
|
|
/// Extension methods for argument parsing
|
|
type MultipleFormsNegation with
|
|
|
|
static member parse'
|
|
(getEnvironmentVariable : string -> string option)
|
|
(args : string list)
|
|
: MultipleFormsNegation
|
|
=
|
|
let ArgParser_errors = ResizeArray ()
|
|
|
|
let helpText () =
|
|
[
|
|
(sprintf
|
|
"%s bool%s%s"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
""
|
|
"")
|
|
]
|
|
|> String.concat "\n"
|
|
|
|
let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
|
|
let mutable arg_0 : bool option = None
|
|
|
|
/// Processes the key-value pair, returning Error if no key was matched.
|
|
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
|
|
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
|
|
let processKeyValue (key : string) (value : string) : Result<unit, string option> =
|
|
if System.String.Equals (key, sprintf "--no-%s" "v", System.StringComparison.OrdinalIgnoreCase) then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> not (System.Boolean.Parse x)) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if System.String.Equals (key, sprintf "--%s" "v", System.StringComparison.OrdinalIgnoreCase) then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> System.Boolean.Parse x) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--no-%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> not (System.Boolean.Parse x)) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> System.Boolean.Parse x) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else
|
|
Error None
|
|
|
|
/// Returns false if we didn't set a value.
|
|
let setFlagValue (key : string) : bool =
|
|
if System.String.Equals (key, sprintf "--no-%s" "v", System.StringComparison.OrdinalIgnoreCase) then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Flag '%s' was supplied multiple times"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- false |> Some
|
|
true
|
|
else if System.String.Equals (key, sprintf "--%s" "v", System.StringComparison.OrdinalIgnoreCase) then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Flag '%s' was supplied multiple times"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- true |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--no-%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Flag '%s' was supplied multiple times"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- false |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Flag '%s' was supplied multiple times"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- true |> Some
|
|
true
|
|
else
|
|
false
|
|
|
|
let rec go (state : ParseState_MultipleFormsNegation) (args : string list) =
|
|
match args with
|
|
| [] ->
|
|
match state with
|
|
| ParseState_MultipleFormsNegation.AwaitingKey -> ()
|
|
| ParseState_MultipleFormsNegation.AwaitingValue key ->
|
|
if setFlagValue key then
|
|
()
|
|
else
|
|
sprintf
|
|
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
|
|
key
|
|
|> ArgParser_errors.Add
|
|
| "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
|
|
| arg :: args ->
|
|
match state with
|
|
| ParseState_MultipleFormsNegation.AwaitingKey ->
|
|
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
|
|
if arg = "--help" then
|
|
helpText () |> failwithf "Help text requested.\n%s"
|
|
else
|
|
let equals = arg.IndexOf (char 61)
|
|
|
|
if equals < 0 then
|
|
args |> go (ParseState_MultipleFormsNegation.AwaitingValue arg)
|
|
else
|
|
let key = arg.[0 .. equals - 1]
|
|
let value = arg.[equals + 1 ..]
|
|
|
|
match processKeyValue key value with
|
|
| Ok () -> go ParseState_MultipleFormsNegation.AwaitingKey args
|
|
| Error x ->
|
|
match x with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process argument %s as key %s and value %s"
|
|
arg
|
|
key
|
|
value
|
|
| Some msg ->
|
|
sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
|
|
go ParseState_MultipleFormsNegation.AwaitingKey args
|
|
else
|
|
arg |> (fun x -> x) |> parser_LeftoverArgs.Add
|
|
go ParseState_MultipleFormsNegation.AwaitingKey args
|
|
| ParseState_MultipleFormsNegation.AwaitingValue key ->
|
|
match processKeyValue key arg with
|
|
| Ok () -> go ParseState_MultipleFormsNegation.AwaitingKey args
|
|
| Error exc ->
|
|
if setFlagValue key then
|
|
go ParseState_MultipleFormsNegation.AwaitingKey (arg :: args)
|
|
else
|
|
match exc with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process supplied arg %s. Help text follows.\n%s"
|
|
key
|
|
(helpText ())
|
|
| Some msg -> msg |> ArgParser_errors.Add
|
|
|
|
go ParseState_MultipleFormsNegation.AwaitingKey args
|
|
|
|
let parser_LeftoverArgs =
|
|
if 0 = parser_LeftoverArgs.Count then
|
|
()
|
|
else
|
|
parser_LeftoverArgs
|
|
|> String.concat " "
|
|
|> sprintf "There were leftover args: %s"
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
|
|
let arg_0 =
|
|
match arg_0 with
|
|
| None ->
|
|
sprintf
|
|
"Required argument '%s' received no value"
|
|
(sprintf "--%s / --%s / --no-%s / --no-%s" "verbose" "v" "verbose" "v")
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
| Some x -> x
|
|
|
|
if 0 = ArgParser_errors.Count then
|
|
{
|
|
VerboseMode = arg_0
|
|
}
|
|
else
|
|
ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
|
|
|
|
static member parse (args : string list) : MultipleFormsNegation =
|
|
MultipleFormsNegation.parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args
|
|
namespace ConsumePlugin
|
|
|
|
open WoofWare.Myriad.Plugins
|
|
|
|
/// Methods to parse arguments for the type CombinedFeatures
|
|
[<AutoOpen>]
|
|
module CombinedFeaturesArgParse =
|
|
type private ParseState_CombinedFeatures =
|
|
/// Ready to consume a key or positional arg
|
|
| AwaitingKey
|
|
/// Waiting to receive a value for the key we've already consumed
|
|
| AwaitingValue of key : string
|
|
|
|
/// Extension methods for argument parsing
|
|
type CombinedFeatures with
|
|
|
|
static member parse'
|
|
(getEnvironmentVariable : string -> string option)
|
|
(args : string list)
|
|
: CombinedFeatures
|
|
=
|
|
let ArgParser_errors = ResizeArray ()
|
|
|
|
let helpText () =
|
|
[
|
|
(sprintf
|
|
"%s bool%s%s"
|
|
(sprintf "--%s / --no-%s" "verbose" "verbose")
|
|
(CombinedFeatures.DefaultVerbose ()
|
|
|> (fun x -> x.ToString ())
|
|
|> sprintf " (default value: %s)")
|
|
"")
|
|
|
|
(sprintf
|
|
"%s bool%s%s"
|
|
(sprintf "--%s / --no-%s" "debug" "debug")
|
|
""
|
|
(sprintf " : %s" ("Enable debug mode")))
|
|
(sprintf "%s bool%s%s" (sprintf "--%s" "normal-bool") "" "")
|
|
]
|
|
|> String.concat "\n"
|
|
|
|
let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
|
|
let mutable arg_0 : bool option = None
|
|
let mutable arg_1 : bool option = None
|
|
let mutable arg_2 : bool option = None
|
|
|
|
/// Processes the key-value pair, returning Error if no key was matched.
|
|
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
|
|
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
|
|
let processKeyValue (key : string) (value : string) : Result<unit, string option> =
|
|
if
|
|
System.String.Equals (key, sprintf "--%s" "normal-bool", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_2 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s" "normal-bool")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--no-%s" "debug", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_1 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "debug" "debug")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_1 <- value |> (fun x -> not (System.Boolean.Parse x)) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "debug", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_1 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "debug" "debug")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_1 <- value |> (fun x -> System.Boolean.Parse x) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--no-%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "verbose" "verbose")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> not (System.Boolean.Parse x)) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf
|
|
"Argument '%s' was supplied multiple times: %s and %s"
|
|
(sprintf "--%s / --no-%s" "verbose" "verbose")
|
|
(x.ToString ())
|
|
(value.ToString ())
|
|
|> ArgParser_errors.Add
|
|
|
|
Ok ()
|
|
| None ->
|
|
try
|
|
arg_0 <- value |> (fun x -> System.Boolean.Parse x) |> Some
|
|
Ok ()
|
|
with _ as exc ->
|
|
exc.Message |> Some |> Error
|
|
else
|
|
Error None
|
|
|
|
/// Returns false if we didn't set a value.
|
|
let setFlagValue (key : string) : bool =
|
|
if
|
|
System.String.Equals (key, sprintf "--%s" "normal-bool", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_2 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "normal-bool")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_2 <- true |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--no-%s" "debug", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_1 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s / --no-%s" "debug" "debug")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_1 <- false |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "debug", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_1 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s / --no-%s" "debug" "debug")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_1 <- true |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--no-%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s / --no-%s" "verbose" "verbose")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- false |> Some
|
|
true
|
|
else if
|
|
System.String.Equals (key, sprintf "--%s" "verbose", System.StringComparison.OrdinalIgnoreCase)
|
|
then
|
|
match arg_0 with
|
|
| Some x ->
|
|
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s / --no-%s" "verbose" "verbose")
|
|
|> ArgParser_errors.Add
|
|
|
|
true
|
|
| None ->
|
|
arg_0 <- true |> Some
|
|
true
|
|
else
|
|
false
|
|
|
|
let rec go (state : ParseState_CombinedFeatures) (args : string list) =
|
|
match args with
|
|
| [] ->
|
|
match state with
|
|
| ParseState_CombinedFeatures.AwaitingKey -> ()
|
|
| ParseState_CombinedFeatures.AwaitingValue key ->
|
|
if setFlagValue key then
|
|
()
|
|
else
|
|
sprintf
|
|
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
|
|
key
|
|
|> ArgParser_errors.Add
|
|
| "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
|
|
| arg :: args ->
|
|
match state with
|
|
| ParseState_CombinedFeatures.AwaitingKey ->
|
|
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
|
|
if arg = "--help" then
|
|
helpText () |> failwithf "Help text requested.\n%s"
|
|
else
|
|
let equals = arg.IndexOf (char 61)
|
|
|
|
if equals < 0 then
|
|
args |> go (ParseState_CombinedFeatures.AwaitingValue arg)
|
|
else
|
|
let key = arg.[0 .. equals - 1]
|
|
let value = arg.[equals + 1 ..]
|
|
|
|
match processKeyValue key value with
|
|
| Ok () -> go ParseState_CombinedFeatures.AwaitingKey args
|
|
| Error x ->
|
|
match x with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process argument %s as key %s and value %s"
|
|
arg
|
|
key
|
|
value
|
|
| Some msg ->
|
|
sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
|
|
go ParseState_CombinedFeatures.AwaitingKey args
|
|
else
|
|
arg |> (fun x -> x) |> parser_LeftoverArgs.Add
|
|
go ParseState_CombinedFeatures.AwaitingKey args
|
|
| ParseState_CombinedFeatures.AwaitingValue key ->
|
|
match processKeyValue key arg with
|
|
| Ok () -> go ParseState_CombinedFeatures.AwaitingKey args
|
|
| Error exc ->
|
|
if setFlagValue key then
|
|
go ParseState_CombinedFeatures.AwaitingKey (arg :: args)
|
|
else
|
|
match exc with
|
|
| None ->
|
|
failwithf
|
|
"Unable to process supplied arg %s. Help text follows.\n%s"
|
|
key
|
|
(helpText ())
|
|
| Some msg -> msg |> ArgParser_errors.Add
|
|
|
|
go ParseState_CombinedFeatures.AwaitingKey args
|
|
|
|
let parser_LeftoverArgs =
|
|
if 0 = parser_LeftoverArgs.Count then
|
|
()
|
|
else
|
|
parser_LeftoverArgs
|
|
|> String.concat " "
|
|
|> sprintf "There were leftover args: %s"
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
|
|
let arg_0 =
|
|
match arg_0 with
|
|
| None -> CombinedFeatures.DefaultVerbose () |> Choice2Of2
|
|
| Some x -> Choice1Of2 x
|
|
|
|
let arg_1 =
|
|
match arg_1 with
|
|
| None ->
|
|
sprintf "Required argument '%s' received no value" (sprintf "--%s / --no-%s" "debug" "debug")
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
| Some x -> x
|
|
|
|
let arg_2 =
|
|
match arg_2 with
|
|
| None ->
|
|
sprintf "Required argument '%s' received no value" (sprintf "--%s" "normal-bool")
|
|
|> ArgParser_errors.Add
|
|
|
|
Unchecked.defaultof<_>
|
|
| Some x -> x
|
|
|
|
if 0 = ArgParser_errors.Count then
|
|
{
|
|
Debug = arg_1
|
|
NormalBool = arg_2
|
|
Verbose = arg_0
|
|
}
|
|
else
|
|
ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
|
|
|
|
static member parse (args : string list) : CombinedFeatures =
|
|
CombinedFeatures.parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args
|