Skip to content

Commit 92b9315

Browse files
authored
feat: Add GetResult(expr, defThunk) (#187)
* Add GetResult(expr, defThunk) * Update ParseResults.fs Fell into same trap! * Add changelog * Add exception trapping + tests
1 parent b6893ea commit 92b9315

File tree

3 files changed

+44
-16
lines changed

3 files changed

+44
-16
lines changed

RELEASE_NOTES.md

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
### 6.1.3
2+
* Add ParseResults.GetResult(expr, unit -> 'T) helper for Catch [#187](https://github.com/fsprojects/Argu/pull/187)
3+
14
### 6.1.2
2-
* Fix Mandatory arguments in nested subcommands. [#116](https://github.com/fsprojects/Argu/issues/116)
3-
* Fix Consistent handling of numeric decimal separators using invariant culture. [#159](https://github.com/fsprojects/Argu/issues/159)
5+
* Fix Mandatory arguments in nested subcommands. [#116](https://github.com/fsprojects/Argu/issues/116) [@chestercodes](https://github.com/chestercodes)
6+
* Fix Consistent handling of numeric decimal separators using invariant culture. [#159](https://github.com/fsprojects/Argu/issues/159) [@stmax82](https://github.com/stmax82)
47

58
### 6.1.1
69
* Fix CustomAssignmentOrSpacedAttribute interop with optional fields.

src/Argu/ParseResults.fs

+25-12
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
100100
/// <summary>Returns the *last* specified parameter of given type.
101101
/// Command line parameters have precedence over AppSettings parameters.</summary>
102102
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
103-
/// <param name="defaultValue">Return this of no parameter of specific kind has been specified.</param>
103+
/// <param name="defaultValue">Return this if no parameter of specific kind has been specified.</param>
104104
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
105105
member s.GetResult ([<ReflectedDefinition>] expr : Expr<'Template>, ?defaultValue : 'Template, ?source : ParseSource) : 'Template =
106106
match defaultValue with
@@ -110,12 +110,25 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
110110
/// <summary>Returns the *last* specified parameter of given type.
111111
/// Command line parameters have precedence over AppSettings parameters.</summary>
112112
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
113-
/// <param name="defaultValue">Return this of no parameter of specific kind has been specified.</param>
113+
/// <param name="defaultValue">Return this if no parameter of specific kind has been specified.</param>
114114
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
115-
member s.GetResult ([<ReflectedDefinition>] expr : Expr<'Fields -> 'Template>, ?defaultValue : 'Fields , ?source : ParseSource) : 'Fields =
115+
member s.GetResult ([<ReflectedDefinition>] expr : Expr<'Fields -> 'Template>, ?defaultValue : 'Fields, ?source : ParseSource) : 'Fields =
116116
match defaultValue with
117117
| None -> let r = getResult source expr in r.FieldContents :?> 'Fields
118-
| Some def -> defaultArg (s.TryGetResult expr) def
118+
| Some def -> defaultArg (s.TryGetResult(expr, ?source = source)) def
119+
120+
/// <summary>Returns the *last* specified parameter of given type.
121+
/// Command line parameters have precedence over AppSettings parameters.</summary>
122+
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
123+
/// <param name="defThunk">Function used to default if no parameter has been specified.
124+
/// Any resulting Exception will be trapped, and the Exception's <c>.Message</c> will be used as the Failure Message as per <c>Raise</c> and <c>Catch</c>.</param>
125+
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
126+
/// <param name="errorCode">The error code to be returned.</param>
127+
/// <param name="showUsage">Print usage together with error message.</param>
128+
member s.GetResult([<ReflectedDefinition>] expr : Expr<'Fields -> 'Template>, defThunk : unit -> 'Fields, ?source : ParseSource, ?errorCode, ?showUsage) : 'Fields =
129+
match s.TryGetResult(expr, ?source = source) with
130+
| Some x -> x
131+
| None -> s.Catch(defThunk, ?errorCode = errorCode, ?showUsage = showUsage)
119132

120133
/// <summary>Checks if parameter of specific kind has been specified.</summary>
121134
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
@@ -130,7 +143,7 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
130143
/// <param name="msg">The error message to be displayed.</param>
131144
/// <param name="errorCode">The error code to be returned.</param>
132145
/// <param name="showUsage">Print usage together with error message.</param>
133-
member _.Raise (msg : string, ?errorCode : ErrorCode, ?showUsage : bool) : 'T =
146+
member _.Raise<'T>(msg : string, ?errorCode : ErrorCode, ?showUsage : bool) : 'T =
134147
let errorCode = defaultArg errorCode ErrorCode.PostProcess
135148
let showUsage = defaultArg showUsage true
136149
error (not showUsage) errorCode msg
@@ -139,15 +152,15 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
139152
/// <param name="error">The error to be displayed.</param>
140153
/// <param name="errorCode">The error code to be returned.</param>
141154
/// <param name="showUsage">Print usage together with error message.</param>
142-
member r.Raise (error : exn, ?errorCode : ErrorCode, ?showUsage : bool) : 'T =
143-
r.Raise (error.Message, ?errorCode = errorCode, ?showUsage = showUsage)
155+
member r.Raise<'T>(error : exn, ?errorCode : ErrorCode, ?showUsage : bool) : 'T =
156+
r.Raise(error.Message, ?errorCode = errorCode, ?showUsage = showUsage)
144157

145-
/// <summary>Handles any raised exception through the argument parser's exiter mechanism. Display usage optionally.</summary>
158+
/// <summary>Handles any raised exception through the argument parser's exiter mechanism.</summary>
146159
/// <param name="f">The operation to be executed.</param>
147160
/// <param name="errorCode">The error code to be returned.</param>
148-
/// <param name="showUsage">Print usage together with error message.</param>
149-
member r.Catch (f : unit -> 'T, ?errorCode : ErrorCode, ?showUsage : bool) : 'T =
150-
try f () with e -> r.Raise(e.Message, ?errorCode = errorCode, ?showUsage = showUsage)
161+
/// <param name="showUsage">Print usage together with error message. Defaults to <c>true</c></param>
162+
member r.Catch<'T>(f : unit -> 'T, ?errorCode : ErrorCode, ?showUsage : bool) : 'T =
163+
try f () with e -> r.Raise(e, ?errorCode = errorCode, ?showUsage = showUsage)
151164

152165
/// <summary>Returns the *last* specified parameter of given type.
153166
/// Command line parameters have precedence over AppSettings parameters.
@@ -252,4 +265,4 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
252265
Unchecked.compare
253266
r.CachedAllResults.Value
254267
other.CachedAllResults.Value
255-
| _ -> invalidArg "other" "cannot compare values of different types"
268+
| _ -> invalidArg "other" "cannot compare values of different types"

tests/Argu.Tests/Tests.fs

+14-2
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ module ``Argu Tests Main List`` =
487487
test <@ nested.Contains <@ Force @> @>
488488

489489
[<Fact>]
490-
let ``Main command parsing should not permit intermittent arguments`` () =
490+
let ``Main command parsing should not permit interstitial arguments`` () =
491491
let args = [|"push" ; "origin" ; "-f" ; "master"|]
492492
raisesWith<ArguParseException> <@ parser.ParseCommandLine(args, ignoreMissing = true) @>
493493
(fun e -> <@ e.FirstLine.Contains "but was '-f'" @>)
@@ -1006,4 +1006,16 @@ module ``Argu Tests Main Primitive`` =
10061006
let results = parser.ParseCommandLine(args, ignoreUnrecognized = true)
10071007
test <@ results.UnrecognizedCliParams = ["foobar"] @>
10081008
test <@ results.Contains <@ Detach @> @>
1009-
test <@ results.GetResult <@ Main @> = "main" @>
1009+
test <@ results.GetResult <@ Main @> = "main" @>
1010+
1011+
[<Fact>]
1012+
let ``Trap defaulting function exceptions`` () =
1013+
let results = parser.ParseCommandLine [| "--mandatory-arg" ; "true"; "command" |]
1014+
let defThunk (): string = failwith "Defaulting Failed"
1015+
raisesWith<ArguParseException>
1016+
<@ results.GetResult(Working_Directory, defThunk, showUsage = false) @>
1017+
<| fun e -> <@ e.Message = "Defaulting Failed" && e.ErrorCode = ErrorCode.PostProcess @>
1018+
raisesWith<ArguParseException>
1019+
<@ results.GetResult(Working_Directory, defThunk) @>
1020+
(fun e -> <@ e.Message.StartsWith "Defaulting Failed" && e.Message.Contains "--working-directory" @>)
1021+

0 commit comments

Comments
 (0)