Skip to content

Instantly share code, notes, and snippets.

@baronfel
Last active September 18, 2023 17:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baronfel/bf84929a8e9eea793090d870b601b2df to your computer and use it in GitHub Desktop.
Save baronfel/bf84929a8e9eea793090d870b601b2df to your computer and use it in GitHub Desktop.
System.CommandLine Flag variant
module CliFlag
open System
open System.CommandLine
open System.CommandLine.Parsing
type CliFlag<'t> =
inherit CliOption<'t>
val private enabledFlagName: string
val private disabledFlagName: string
val private defaultValue: 't
val private enabledValue: 't
val private disabledValue: 't
new(flagName: string, enabledValue: 't, disabledValue: 't, defaultValue: 't) as this =
ArgumentNullException.ThrowIfNullOrWhiteSpace(flagName, nameof (flagName))
if
flagName.StartsWith("-")
|| flagName.StartsWith("--")
then
let e =
ArgumentException("Flag name must not start with '-' or '--'", nameof (flagName))
raise e
{ inherit CliOption<'t>(null, [||])
enabledFlagName = flagName
disabledFlagName = flagName
enabledValue = enabledValue
disabledValue = disabledValue
defaultValue = defaultValue } // dummy branch, will never be hit
else
let tfn = "--" + flagName
let ffn = "--no-" + flagName
{ inherit CliOption<'t>(tfn, [| ffn |], Arity = ArgumentArity.Zero, CustomParser = this.Parse)
enabledFlagName = tfn
disabledFlagName = ffn
enabledValue = enabledValue
disabledValue = disabledValue
defaultValue = defaultValue }
abstract member Parse: r: ArgumentResult -> 't
default x.Parse(r: ArgumentResult) : 't =
let parent = r.Parent :?> OptionResult
match parent.IdentifierToken.Value with
| s when s = x.enabledFlagName -> x.enabledValue
| s when s = x.disabledFlagName -> x.disabledValue
| s -> failwith $"Unexpected flag name %s{s}"
type CliFlag(name, defaultValue) =
inherit CliFlag<bool>(name, true, false, defaultValue)
module Flags
open System
open System.CommandLine
open System.CommandLine.Parsing
let selfContained = CliFlag("self-contained", defaultValue = false)
let private isCiEnvironment () =
Environment.GetEnvironmentVariable("CI") = "true"
let private shellSaysWeAreInteractive () = true
let private detectInteractiveState () =
not Console.IsInputRedirected
|| not (isCiEnvironment ())
|| shellSaysWeAreInteractive ()
let interactive =
{ new CliFlag("interactive", defaultValue = true) with
member x.Parse(r: ArgumentResult) =
// how should we probe for shell state here?
if (r.Parent :?> OptionResult).Implicit then
detectInteractiveState ()
else
true }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment