diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index a3f2c4ff..5ee49664 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -2,8 +2,9 @@
* Fix NRE in derivation of programName introduced in 6.2.2 [#292](https://github.com/SwensenSoftware/unquote/pull/292) [@dimension-zero](https://github.com/dimension-zero)
* Fix Clarify exception in expr2Uci [#293](https://github.com/SwensenSoftware/unquote/pull/293) [@dimension-zero](https://github.com/dimension-zero)
* Fix Report all missing args in error message, not just first level [#297](https://github.com/SwensenSoftware/unquote/pull/297) [@dimension-zero](https://github.com/dimension-zero)
-* Add `Argu.Samples.Introspect` sample [#298](https://github.com/SwensenSoftware/unquote/pull/298) [@dimension-zero](https://github.com/dimension-zero)
* Fix Limit min wordwrap column to 20 [#302](https://github.com/SwensenSoftware/unquote/pull/302) [@dimension-zero](https://github.com/dimension-zero)
+* Add `Argu.Samples.Introspect` sample [#298](https://github.com/SwensenSoftware/unquote/pull/298) [@dimension-zero](https://github.com/dimension-zero)
+* Add `ArgumentParser.Parse(ParseConfig)` [#307](https://github.com/SwensenSoftware/unquote/pull/307) [@dimension-zero](https://github.com/dimension-zero)
### 6.2.5
* Drop Package `FSharp.Core` dependency to `6.0.0` [#264](https://github.com/fsprojects/Argu/pull/264)
diff --git a/src/Argu/ArgumentParser.fs b/src/Argu/ArgumentParser.fs
index f5816773..cf185651 100644
--- a/src/Argu/ArgumentParser.fs
+++ b/src/Argu/ArgumentParser.fs
@@ -3,6 +3,35 @@
open FSharp.Quotations
open Argu.UnionArgInfo
+/// Configuration record for .
+/// Each field carries the same meaning as the matching optional parameter on
+/// the existing Parse overload. Use
+/// as a starting point and override only the fields you care about.
+[]
+type ParseConfig =
+ {
+ /// The command line input. None takes the inputs from System.Environment.
+ Inputs : string [] option
+ /// Configuration reader used to source AppSettings-style arguments.
+ /// None uses the AppSettings configuration of the current process.
+ ConfigurationReader : IConfigurationReader option
+ /// Ignore errors caused by the Mandatory attribute.
+ IgnoreMissing : bool
+ /// Ignore CLI arguments that do not match the schema.
+ IgnoreUnrecognized : bool
+ /// Treat '--help' parameters as parse errors.
+ RaiseOnUsage : bool
+ }
+ /// Default parse configuration, matching the historical Parse(...) defaults:
+ /// inputs and configurationReader inherited from the environment, do not ignore
+ /// missing or unrecognized arguments, and raise on '--help'.
+ static member Default : ParseConfig =
+ { Inputs = None
+ ConfigurationReader = None
+ IgnoreMissing = false
+ IgnoreUnrecognized = false
+ RaiseOnUsage = true }
+
/// The Argu type generates an argument parser given a type argument
/// that is an F# discriminated union. It can then be used to parse command line arguments
/// or XML configuration.
@@ -156,6 +185,19 @@ and []
with ParserExn (errorCode, msg) -> errorHandler.Exit (msg, errorCode)
+ /// Parse both command line args and supplied configuration reader, using
+ /// a record. Useful when callers want to construct
+ /// the parameter set programmatically (e.g. layering host defaults over user
+ /// overrides) without juggling many optional method arguments.
+ /// The parse configuration. See ParseConfig.Default.
+ member self.Parse (config : ParseConfig) : ParseResults<'Template> =
+ self.Parse(
+ ?inputs = config.Inputs,
+ ?configurationReader = config.ConfigurationReader,
+ ignoreMissing = config.IgnoreMissing,
+ ignoreUnrecognized = config.IgnoreUnrecognized,
+ raiseOnUsage = config.RaiseOnUsage)
+
/// Parse both command line args and supplied configuration reader.
/// Results are merged with command line args overriding configuration parameters.
/// The command line input. Taken from System.Environment if not specified.
@@ -229,8 +271,8 @@ and []
mkCommandLineArgs argInfo (Seq.cast args) |> Seq.toArray
/// Prints parameters in command line format. Useful for argument string generation.
- member ap.PrintCommandLineArgumentsFlat (args : 'Template list) : string =
- ap.PrintCommandLineArguments args |> flattenCliTokens
+ member self.PrintCommandLineArgumentsFlat (args : 'Template list) : string =
+ self.PrintCommandLineArguments args |> flattenCliTokens
/// Prints parameters in App.Config format.
/// The parameters that fill out the XML document.
diff --git a/tests/Argu.Tests/Argu.Tests.fsproj b/tests/Argu.Tests/Argu.Tests.fsproj
index 343246e8..9cb686f7 100644
--- a/tests/Argu.Tests/Argu.Tests.fsproj
+++ b/tests/Argu.Tests/Argu.Tests.fsproj
@@ -7,6 +7,7 @@
+
diff --git a/tests/Argu.Tests/ParseConfigTests.fs b/tests/Argu.Tests/ParseConfigTests.fs
new file mode 100644
index 00000000..68edf22e
--- /dev/null
+++ b/tests/Argu.Tests/ParseConfigTests.fs
@@ -0,0 +1,85 @@
+module Argu.Tests.ParseConfigTests
+
+open System.Collections.Generic
+open Swensen.Unquote
+open Xunit
+
+open Argu
+
+type Args =
+ | [] Port of int
+ | Verbose
+ | Tag of string
+ interface IArgParserTemplate with
+ member this.Usage =
+ match this with
+ | Port _ -> "port"
+ | Verbose -> "verbose"
+ | Tag _ -> "tag"
+
+let private parser () = ArgumentParser.Create()
+[]
+let ``ParseConfig.Default holds historical defaults`` () =
+ let d = ParseConfig.Default
+ test <@ d.Inputs = None @>
+ test <@ d.ConfigurationReader = None @>
+ test <@ d.IgnoreMissing = false @>
+ test <@ d.IgnoreUnrecognized = false @>
+ test <@ d.RaiseOnUsage = true @>
+
+[]
+let ``Parse(config with explicit inputs) parses those inputs`` () =
+ let p = parser ()
+ let argv = [| "--port"; "8080"; "--verbose" |]
+ let cfg = { ParseConfig.Default with Inputs = Some argv ; RaiseOnUsage = false }
+ let results = p.Parse(cfg)
+ test <@ results.GetResult(Port) = 8080 @>
+ test <@ results.Contains(Verbose) @>
+
+[]
+let ``Parse(config) matches Parse(?inputs, ...) for the same parameters`` () =
+ let p = parser ()
+ let argv = [| "--port"; "1234"; "--tag"; "v1" |]
+ let viaConfig =
+ let cfg = { ParseConfig.Default with Inputs = Some argv ; RaiseOnUsage = false }
+ p.Parse(cfg)
+ let viaOptional = p.Parse(inputs = argv, raiseOnUsage = false)
+ test <@ viaConfig.GetResult(Port) = viaOptional.GetResult(Port) @>
+ test <@ viaConfig.GetResult(Tag) = viaOptional.GetResult(Tag) @>
+
+[]
+let ``Parse(config with IgnoreMissing=true) skips mandatory check`` () =
+ let p = parser ()
+ let cfg = { ParseConfig.Default with Inputs = Some [||] ; IgnoreMissing = true }
+ let results = p.Parse(cfg)
+ test <@ results.TryGetResult(Port) = None @>
+
+[]
+let ``Parse(config with IgnoreUnrecognized=true) collects unknown args`` () =
+ let p = parser ()
+ let cfg =
+ { ParseConfig.Default with
+ Inputs = Some [| "--port"; "1"; "--bogus" |]
+ IgnoreUnrecognized = true
+ RaiseOnUsage = false }
+ let results = p.Parse(cfg)
+ test <@ results.UnrecognizedCliParams |> List.contains "--bogus" @>
+
+/// Argu's missing-mandatory check fires from the CLI even when AppSettings provides a value (pre-existing behavior),
+/// so the AppSettings round-trip test uses a non-mandatory schema.
+type AppSettingsArgs =
+ | TagKey of string
+ interface IArgParserTemplate with member this.Usage = "tag"
+
+[]
+let ``Parse(config with ConfigurationReader) sources AppSettings`` () =
+ let p = ArgumentParser.Create()
+ let dict = Dictionary()
+ dict["tagkey"] <- "v1"
+ let reader = ConfigurationReader.FromDictionary dict
+ let cfg =
+ { ParseConfig.Default with
+ Inputs = Some [||]
+ ConfigurationReader = Some reader }
+ let results = p.Parse(cfg)
+ test <@ results.GetResult(TagKey) = "v1" @>