Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add GetResult(expr, defThunk) #187

Merged
merged 4 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
### 6.1.3
* Add ParseResults.GetResult(expr, unit -> 'T) helper for Catch [#187](https://github.com/fsprojects/Argu/pull/187)

### 6.1.2
* Fix Mandatory arguments in nested subcommands. [#116](https://github.com/fsprojects/Argu/issues/116)
* Fix Consistent handling of numeric decimal separators using invariant culture. [#159](https://github.com/fsprojects/Argu/issues/159)
* Fix Mandatory arguments in nested subcommands. [#116](https://github.com/fsprojects/Argu/issues/116) [@chestercodes](https://github.com/chestercodes)
* Fix Consistent handling of numeric decimal separators using invariant culture. [#159](https://github.com/fsprojects/Argu/issues/159) [@stmax82](https://github.com/stmax82)

### 6.1.1
* Fix CustomAssignmentOrSpacedAttribute interop with optional fields.
Expand Down
37 changes: 25 additions & 12 deletions src/Argu/ParseResults.fs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
/// <summary>Returns the *last* specified parameter of given type.
/// Command line parameters have precedence over AppSettings parameters.</summary>
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
/// <param name="defaultValue">Return this of no parameter of specific kind has been specified.</param>
/// <param name="defaultValue">Return this if no parameter of specific kind has been specified.</param>
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
member s.GetResult ([<ReflectedDefinition>] expr : Expr<'Template>, ?defaultValue : 'Template, ?source : ParseSource) : 'Template =
match defaultValue with
Expand All @@ -110,12 +110,25 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
/// <summary>Returns the *last* specified parameter of given type.
/// Command line parameters have precedence over AppSettings parameters.</summary>
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
/// <param name="defaultValue">Return this of no parameter of specific kind has been specified.</param>
/// <param name="defaultValue">Return this if no parameter of specific kind has been specified.</param>
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
member s.GetResult ([<ReflectedDefinition>] expr : Expr<'Fields -> 'Template>, ?defaultValue : 'Fields , ?source : ParseSource) : 'Fields =
member s.GetResult ([<ReflectedDefinition>] expr : Expr<'Fields -> 'Template>, ?defaultValue : 'Fields, ?source : ParseSource) : 'Fields =
match defaultValue with
| None -> let r = getResult source expr in r.FieldContents :?> 'Fields
| Some def -> defaultArg (s.TryGetResult expr) def
| Some def -> defaultArg (s.TryGetResult(expr, ?source = source)) def
nojaf marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>Returns the *last* specified parameter of given type.
/// Command line parameters have precedence over AppSettings parameters.</summary>
/// <param name="expr">The name of the parameter, expressed as quotation of DU constructor.</param>
/// <param name="defThunk">Function used to default if no parameter has been specified.
/// 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>
/// <param name="source">Optional source restriction: AppSettings or CommandLine.</param>
/// <param name="errorCode">The error code to be returned.</param>
/// <param name="showUsage">Print usage together with error message.</param>
member s.GetResult([<ReflectedDefinition>] expr : Expr<'Fields -> 'Template>, defThunk : unit -> 'Fields, ?source : ParseSource, ?errorCode, ?showUsage) : 'Fields =
match s.TryGetResult(expr, ?source = source) with
| Some x -> x
| None -> s.Catch(defThunk, ?errorCode = errorCode, ?showUsage = showUsage)

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

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

/// <summary>Returns the *last* specified parameter of given type.
/// Command line parameters have precedence over AppSettings parameters.
Expand Down Expand Up @@ -252,4 +265,4 @@ type ParseResults<[<EqualityConditionalOn; ComparisonConditionalOn>]'Template wh
Unchecked.compare
r.CachedAllResults.Value
other.CachedAllResults.Value
| _ -> invalidArg "other" "cannot compare values of different types"
| _ -> invalidArg "other" "cannot compare values of different types"
16 changes: 14 additions & 2 deletions tests/Argu.Tests/Tests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ module ``Argu Tests Main List`` =
test <@ nested.Contains <@ Force @> @>

[<Fact>]
let ``Main command parsing should not permit intermittent arguments`` () =
let ``Main command parsing should not permit interstitial arguments`` () =
let args = [|"push" ; "origin" ; "-f" ; "master"|]
raisesWith<ArguParseException> <@ parser.ParseCommandLine(args, ignoreMissing = true) @>
(fun e -> <@ e.FirstLine.Contains "but was '-f'" @>)
Expand Down Expand Up @@ -1006,4 +1006,16 @@ module ``Argu Tests Main Primitive`` =
let results = parser.ParseCommandLine(args, ignoreUnrecognized = true)
test <@ results.UnrecognizedCliParams = ["foobar"] @>
test <@ results.Contains <@ Detach @> @>
test <@ results.GetResult <@ Main @> = "main" @>
test <@ results.GetResult <@ Main @> = "main" @>

[<Fact>]
let ``Trap defaulting function exceptions`` () =
let results = parser.ParseCommandLine [| "--mandatory-arg" ; "true"; "command" |]
let defThunk (): string = failwith "Defaulting Failed"
raisesWith<ArguParseException>
<@ results.GetResult(Working_Directory, defThunk, showUsage = false) @>
<| fun e -> <@ e.Message = "Defaulting Failed" && e.ErrorCode = ErrorCode.PostProcess @>
raisesWith<ArguParseException>
<@ results.GetResult(Working_Directory, defThunk) @>
(fun e -> <@ e.Message.StartsWith "Defaulting Failed" && e.Message.Contains "--working-directory" @>)