Skip to content

Commit a28d042

Browse files
committed
- Added new Input fns: validateFileExists and validateDirectoryExists.
- Added a couple Input alias functions - Added description comments to Input functions - Simplified TestConsole usage by removing launchSettings.json
1 parent d12b75f commit a28d042

12 files changed

+103
-66
lines changed

src/FSharp.SystemCommandLine/Inputs.fs

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,50 +46,65 @@ type ActionInput<'T>(inputType: ActionInputSource) =
4646

4747
module Input =
4848

49+
/// Injects an `ActionContext` into the action which contains the `ParseResult` and a cancellation token.
4950
let context =
5051
ActionInput<ActionContext>(Context)
5152

53+
/// Creates a named option. Example: `option "--file-name"`
5254
let option<'T> (name: string) =
5355
Option<'T>(name) |> ActionInput.OfOption
5456

57+
/// Edits the underlying System.CommandLine.Option<'T>.
5558
let editOption (edit: Option<'T> -> unit) (input: ActionInput<'T>) =
5659
match input.Source with
5760
| ParsedOption o -> o :?> Option<'T> |> edit
5861
| _ -> ()
5962
input
6063

64+
/// Edits the underlying System.CommandLine.Argument<'T>.
6165
let editArgument (edit: Argument<'T> -> unit) (input: ActionInput<'T>) =
6266
match input.Source with
6367
| ParsedArgument a -> a :?> Argument<'T> |> edit
6468
| _ -> ()
6569
input
6670

71+
/// Adds one or more aliases to an option.
6772
let aliases (aliases: string seq) (input: ActionInput<'T>) =
6873
input |> editOption (fun o -> aliases |> Seq.iter o.Aliases.Add)
6974

75+
/// Adds an alias to an option.
7076
let alias (alias: string) (input: ActionInput<'T>) =
7177
input |> editOption (fun o -> o.Aliases.Add alias)
7278

73-
let desc (description: string) (input: ActionInput<'T>) =
79+
/// Sets the description of an option or argument.
80+
let description (description: string) (input: ActionInput<'T>) =
7481
input
7582
|> editOption (fun o -> o.Description <- description)
7683
|> editArgument (fun a -> a.Description <- description)
7784

85+
/// An alias for `description` to set the description of the input.
86+
let desc = description
87+
88+
/// Sets the default value of an option or argument.
7889
let defaultValue (defaultValue: 'T) (input: ActionInput<'T>) =
7990
input
8091
|> editOption (fun o -> o.DefaultValueFactory <- (fun _ -> defaultValue))
8192
|> editArgument (fun a -> a.DefaultValueFactory <- (fun _ -> defaultValue))
8293

94+
/// An alias for `defaultValue` to set the default value of an option or argument.
8395
let def = defaultValue
8496

85-
let defFactory (defaultValueFactory: Parsing.ArgumentResult -> 'T) (input: ActionInput<'T>) =
97+
/// Sets the default value factory of an option or argument.
98+
let defaultValueFactory (defaultValueFactory: Parsing.ArgumentResult -> 'T) (input: ActionInput<'T>) =
8699
input
87100
|> editOption (fun o -> o.DefaultValueFactory <- defaultValueFactory)
88101
|> editArgument (fun a -> a.DefaultValueFactory <- defaultValueFactory)
89102

103+
/// Marks an option as required.
90104
let required (input: ActionInput<'T>) =
91105
input |> editOption (fun o -> o.Required <- true)
92106

107+
/// Creates a named option of type `Option<'T option>` that defaults to `None`.
93108
let optionMaybe<'T> (name: string) =
94109
let o = Option<'T option>(name, aliases = [||])
95110
let isBool = typeof<'T> = typeof<bool>
@@ -104,10 +119,12 @@ module Input =
104119
o.DefaultValueFactory <- (fun _ -> None)
105120
ActionInput.OfOption<'T option> o
106121

122+
/// Creates a named argument. Example: `argument "file-name"`
107123
let argument<'T> (name: string) =
108124
let a = Argument<'T>(name)
109125
ActionInput.OfArgument<'T> a
110126

127+
/// Creates a named argument of type `Argument<'T option>` that defaults to `None`.
111128
let argumentMaybe<'T> (name: string) =
112129
let a = Argument<'T option>(name)
113130
a.DefaultValueFactory <- (fun _ -> None)
@@ -119,6 +136,7 @@ module Input =
119136
)
120137
ActionInput.OfArgument<'T option> a
121138

139+
/// Adds a validator that validates the parsed value of an option or argument.
122140
let validate (validate: 'T -> Result<unit, string>) (input: ActionInput<'T>) =
123141
input
124142
|> editOption (fun o ->
@@ -138,15 +156,33 @@ module Input =
138156
)
139157
)
140158

159+
/// Validates that the file exists.
160+
let validateFileExists (input: ActionInput<System.IO.FileInfo>) =
161+
input
162+
|> validate (fun file ->
163+
if file.Exists then Ok ()
164+
else Error $"File '{file.FullName}' does not exist."
165+
)
166+
167+
/// Validates that the directory exists.
168+
let validateDirectoryExists (input: ActionInput<System.IO.DirectoryInfo>) =
169+
input
170+
|> validate (fun dir ->
171+
if dir.Exists then Ok ()
172+
else Error $"Directory '{dir.FullName}' does not exist."
173+
)
174+
175+
/// Adds a validator for the given `Parsing.SymbolResult`.
141176
let addValidator (validator: Parsing.SymbolResult -> unit) (input: ActionInput<'T>) =
142177
input
143178
|> editOption (fun o -> o.Validators.Add(validator))
144179
|> editArgument (fun a -> a.Validators.Add(validator))
145180

146-
181+
/// Converts an `Option<'T>` to an `ActionInput<'T>` for usage with the command builders.
147182
let ofOption (o: Option<'T>) =
148183
ActionInput.OfOption<'T> o
149184

185+
/// Converts an `Argument<'T>` to an `ActionInput<'T>` for usage with the command builders.
150186
let ofArgument (a: Argument<'T>) =
151187
ActionInput.OfArgument<'T> a
152188

src/TestConsole/Program.fs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
11
module Program
22

3-
open FSharp.SystemCommandLine
4-
open Input
3+
open System.CommandLine.Parsing
54

6-
let app (words: string array, separator: string option) =
7-
let separator = separator |> Option.defaultValue ", "
8-
System.String.Join(separator, words) |> printfn "Result: %s"
5+
let args commandLine =
6+
CommandLineParser.SplitCommandLine(commandLine) |> Seq.toArray
97

108
[<EntryPoint>]
11-
let main argv =
12-
//let words = Input.Option("word", ["--word"; "-w"], Array.empty, "A list of words to be appended")
13-
//let separator = Input.OptionMaybe("separator", ["--separator"; "-s"], "A character that will separate the joined words.")
14-
let words = option "--word" |> alias "-w" |> desc "A list of words to be appended"
15-
let separator = optionMaybe "--separator" |> alias "-s" |> desc "A character that will separate the joined words."
16-
17-
rootCommand argv {
18-
description "Appends words together"
19-
inputs (words, separator)
20-
setAction app
21-
}
9+
let main _ =
10+
11+
//ProgramAlt1.main (args "--int-option 1 --bool-option true --file-option \"c:\test\"")
12+
13+
//ProgramNoArgs.main (args "")
14+
15+
//ProgramNestedSubCommands.main (args "io list \"c:/data/\" --enable-logging") // Also contains global options
16+
17+
//ProgramSubCommand.main (args "list c:/data/")
18+
//ProgramSubCommand.main (args "delete c:/data/ --recursive")
19+
20+
//ProgramTask.main (args "-w hello -w world")
21+
22+
//ProgramTokenReplacer.main (args "--package @shoelace-style/shoelace")
23+
24+
//ProgramExtraInputs.main (args "-a A -b B -c C -d D -e E -1 1 -2 2 -3 3 -4 4 -5 5")
25+
26+
ProgramAppendWords.main (args "-w hello -w world -s \", \"")

src/TestConsole/ProgramAlt1.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ let action (parseResult: ParseResult) (cancel: CancellationToken) = task {
2121
return 5
2222
}
2323

24-
//[<EntryPoint>]
2524
let main (argv: string[]) =
2625
let cmd = RootCommand()
2726
cmd.Description <- "My sample app"

src/TestConsole/ProgramAppendWords.fs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
module ProgramAppendWords
2+
3+
open FSharp.SystemCommandLine
4+
open Input
5+
6+
let app (words: string array, separator: string option) =
7+
let separator = separator |> Option.defaultValue ", "
8+
System.String.Join(separator, words) |> printfn "Result: %s"
9+
10+
let main argv =
11+
//let words = Input.Option("word", ["--word"; "-w"], Array.empty, "A list of words to be appended")
12+
//let separator = Input.OptionMaybe("separator", ["--separator"; "-s"], "A character that will separate the joined words.")
13+
let words = option "--word" |> alias "-w" |> desc "A list of words to be appended"
14+
let separator = optionMaybe "--separator" |> alias "-s" |> desc "A character that will separate the joined words."
15+
16+
rootCommand argv {
17+
description "Appends words together"
18+
inputs (words, separator)
19+
setAction app
20+
}

src/TestConsole/ProgramExtraInputs.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ let app ctx =
3232
|> printfn "Result: %s"
3333
0
3434

35-
//[<EntryPoint>]
3635
let main argv =
3736
rootCommand argv {
3837
description "Appends words together"

src/TestConsole/ProgramNestedSubCommands.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ let ioCmd =
6868
addCommands [ deleteCmd; listCmd ]
6969
}
7070

71-
//[<EntryPoint>]
7271
let main (argv: string[]) =
7372
let cfg =
7473
commandLineConfiguration {

src/TestConsole/ProgramNoArgs.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ open FSharp.SystemCommandLine
55
let app () =
66
printfn "Do stuff"
77

8-
//[<EntryPoint>]
98
let main argv =
109
rootCommand argv {
1110
description "My sample app"

src/TestConsole/ProgramSubCommand.fs

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,42 +6,40 @@ open Input
66

77
let listCmd =
88
let action (dir: DirectoryInfo) =
9-
if dir.Exists then
10-
dir.EnumerateFiles()
11-
|> Seq.iter (fun f -> printfn "%s" f.FullName)
12-
else
13-
printfn $"{dir.FullName} does not exist."
14-
15-
let dir = argument "directory" |> def (DirectoryInfo @"c:\default")
9+
dir.EnumerateFiles()
10+
|> Seq.iter (fun f -> printfn "%s" f.FullName)
1611

1712
command "list" {
1813
description "lists contents of a directory"
19-
inputs dir
14+
inputs (
15+
argument "directory"
16+
|> defaultValue (DirectoryInfo @"c:\default")
17+
|> validateDirectoryExists
18+
)
2019
setAction action
2120
addAlias "ls"
2221
}
2322

2423
let deleteCmd =
2524
let action (dir: DirectoryInfo, recursive: bool) =
26-
if dir.Exists then
27-
if recursive
28-
then printfn $"Recursively deleting {dir.FullName}"
29-
else printfn $"Deleting {dir.FullName}"
30-
else
31-
printfn $"{dir.FullName} does not exist."
32-
33-
let dir = argument "directory" |> def (DirectoryInfo @"c:\default")
34-
let recursive = option "--recursive" |> def false
25+
if recursive
26+
then printfn $"Recursively deleting {dir.FullName}"
27+
else printfn $"Deleting {dir.FullName}"
3528

3629
command "delete" {
3730
description "deletes a directory"
38-
inputs (dir, recursive)
31+
inputs (
32+
argument "directory"
33+
|> defaultValue (DirectoryInfo @"c:\default")
34+
|> validateDirectoryExists,
35+
36+
option "--recursive"
37+
|> defaultValue false
38+
)
3939
setAction action
4040
addAliases [ "d"; "del" ]
4141
}
4242

43-
44-
// [<EntryPoint>]
4543
let main argv =
4644
rootCommand argv {
4745
description "File System Manager"

src/TestConsole/ProgramTask.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ let app (ctx, words: string array, separator: string) =
1818
|> printfn "Result: %s"
1919
}
2020

21-
//[<EntryPoint>]
2221
let main argv =
2322
let words = option "--word" |> alias "-w" |> desc "A list of words to be appended"
2423
let separator = option "--separator" |> alias "-s" |> def ", " |> desc "A character that will separate the joined words."

src/TestConsole/ProgramTokenReplacer.fs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ let app (package: string) =
1111
eprintfn "The package name does not start with a leading @"
1212
1
1313

14-
//[<EntryPoint>]
1514
let main argv =
1615

1716
// The package option needs to accept strings that start with "@" symbol.

0 commit comments

Comments
 (0)