-
Notifications
You must be signed in to change notification settings - Fork 323
Expand file tree
/
Copy pathCommandLine.fs
More file actions
169 lines (142 loc) · 6.68 KB
/
CommandLine.fs
File metadata and controls
169 lines (142 loc) · 6.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// We expose the main System.CommandLine namespace types through this
// namespace so that we can alias the `System.CommandLine.Option` type.
namespace System.CommandLine.FSharp
open System.CommandLine.Parsing
type Argument = System.CommandLine.Argument
type ArgumentArity = System.CommandLine.ArgumentArity
type ArgumentValidation = System.CommandLine.ArgumentValidation
type Argument<'T> = System.CommandLine.Argument<'T>
type Command = System.CommandLine.Command
type CompletionSourceExtensions = System.CommandLine.CompletionSourceExtensions
type DiagramDirective = System.CommandLine.DiagramDirective
type Directive = System.CommandLine.Directive
type EnvironmentVariablesDirective = System.CommandLine.EnvironmentVariablesDirective
type InvocationConfiguration = System.CommandLine.InvocationConfiguration
type CommandOption = System.CommandLine.Option
type CommandOptionValidation = System.CommandLine.OptionValidation
type CommandOption<'T> = System.CommandLine.Option<'T>
type ParserConfiguration = System.CommandLine.ParserConfiguration
type ParseResult = System.CommandLine.ParseResult
type RootCommand = System.CommandLine.RootCommand
type Symbol = System.CommandLine.Symbol
type VersionOption = System.CommandLine.VersionOption
module Utils =
// Static type resolution is used since ParseResult and SymbolResult share some members but are
// not within a type hierarchy.
// If a command, argument, or option is not present, then we receive ValueNone
// If the above has a default value, then we will receive a value.
let inline getCommandResult<'ParseResult when 'ParseResult: (member GetResult: Command -> CommandResult)>
(command: Command)
: 'ParseResult -> CommandResult voption
=
_.GetResult(command) >> ValueOption.ofObj
let inline getArgumentResult<'ParseResult when 'ParseResult: (member GetResult: Argument -> ArgumentResult)>
(command: Argument)
: 'ParseResult -> ArgumentResult voption
=
_.GetResult(command) >> ValueOption.ofObj
let inline getOptionResult<'T, 'ParseResult
when 'T :> CommandOption and 'ParseResult: (member GetResult: CommandOption -> OptionResult)>
(command: 'T)
: 'ParseResult -> OptionResult voption
=
_.GetResult(command :> CommandOption) >> ValueOption.ofObj
let inline getNamedResult<'ParseResult when 'ParseResult: (member GetResult: string -> SymbolResult)>
(command: string)
: 'ParseResult -> SymbolResult voption
=
_.GetResult(command) >> ValueOption.ofObj
let inline getArgumentValue<'T, 'ParseResult when 'ParseResult: (member GetValue: Argument<'T> -> 'T)>
(arg: Argument<'T>)
: 'ParseResult -> 'T voption
=
_.GetValue(arg)
>> function
| value when box value |> isNull -> ValueNone
| value -> ValueSome value
let inline getOptionValue<'T, 'ParseResult when 'ParseResult: (member GetValue: CommandOption<'T> -> 'T)>
(cmdOption: CommandOption<'T>)
: 'ParseResult -> 'T voption
=
_.GetValue(cmdOption)
>> function
| value when box value |> isNull -> ValueNone
| value -> ValueSome value
let inline getNamedValue<'ParseResult when 'ParseResult: (member GetValue: string -> obj)>
(arg: string)
: 'ParseResult -> obj voption
=
_.GetValue(arg)
>> function
| value when box value |> isNull -> ValueNone
| value -> ValueSome value
// Explicit warning and hint that we are mutating C# objects. Warning is for posterity
module Mutate =
let inline description desc (symbol: #Symbol) : #Symbol =
symbol.Description <- desc
symbol
let inline hide (symbol: #Symbol) : #Symbol =
symbol.Hidden <- true
symbol
module CommandOption =
let description = description
let hide = hide
let addAlias alias (opt: #CommandOption) : #CommandOption =
opt.Aliases.Add alias
opt
let require (opt: #CommandOption) : #CommandOption =
opt.Required <- true
opt
let recursive (opt: #CommandOption) : #CommandOption =
opt.Recursive <- true
opt
// It seems these are noops in the current beta of System.CommandLine
let filePathsOnly (opt: CommandOption<string>) : CommandOption<string> = opt.AcceptLegalFilePathsOnly()
let fileNamesOnly (opt: CommandOption<string>) : CommandOption<string> = opt.AcceptLegalFileNamesOnly()
//
let valueOneOf (values: 'T seq) (opt: CommandOption<'T>) : CommandOption<'T> =
opt.AcceptOnlyFromAmong(values |> Seq.map _.ToString() |> Seq.toArray)
let valueOneOfStrings (values: string seq) (opt: CommandOption<'T>) : CommandOption<'T> =
opt.AcceptOnlyFromAmong(values |> Seq.toArray)
let arity (value: ArgumentArity) (opt: #CommandOption) : #CommandOption =
opt.Arity <- value
opt
// Messing with the argument result in the factory func can cause issues and is mostly unneeded
let defaultValue (value: 'T) (opt: CommandOption<'T>) : CommandOption<'T> =
opt.DefaultValueFactory <- (fun _ -> value)
opt
// Useful for options like `--language` which has many possible values (abbrevs and full names) and we want to
// only have a select few written in the help message
let helpName (msg: string) (opt: CommandOption<'T>) : CommandOption<'T> =
opt.HelpName <- msg
opt
module Argument =
let description = description
let hide = hide
let arity (value: ArgumentArity) (opt: #Argument) : #Argument =
opt.Arity <- value
opt
let defaultValue (value: 'T) (arg: Argument<'T>) : Argument<'T> =
arg.DefaultValueFactory <- (fun _ -> value)
arg
module Command =
let description = description
let hide = hide
let addAlias alias (cmd: #Command) : #Command =
cmd.Aliases.Add alias
cmd
let mapAction (func: ParseResult -> int) (cmd: #Command) : #Command =
cmd.SetAction(func)
cmd
let iterAction (func: ParseResult -> int) = mapAction func >> ignore
module CommandOption =
let create<'T> name = CommandOption<'T>(name)
module Argument =
let create<'T> name = Argument<'T>(name)
module RootCommand =
let create description = RootCommand(description)
module Command =
let create name = Command(name)
let parse (argv: string array) : #Command -> ParseResult = _.Parse(argv)
let invoke: ParseResult -> int = _.Invoke()
let parseAndInvoke argv = parse argv >> invoke