diff --git a/README.md b/README.md index 6e94a2b..aa09824 100644 --- a/README.md +++ b/README.md @@ -374,17 +374,17 @@ Where `T` is the config type, `opt` is the option name, and `msg` is the value of the `obsolete` pragma (which may be empty). If the logger requires initialization, it can be set through -the `loggerSetup` parameter, for example: +the `onLoaded` parameter, for example: ```nim type MyConf = object # options -proc myLogger(config: MyConf) {.gcsafe, raises: [ConfigurationError].} = +proc myLogger(config: var MyConf) {.gcsafe, raises: [ConfigurationError].} = # set up logger discard -let c = MyConf.load(loggerSetup = myLogger) +let c = MyConf.load(onLoaded = myLogger) ``` ----------------- @@ -500,6 +500,24 @@ If you provide this parameter, Confutils will automatically respond to the standard `--version` switch. If sub-commands are used, an additional `version` top-level command will be inserted as well. +## Post processing + +The `onLoaded` parameter in `load` can be used for post-processing +the configuration after it's fully populated from CLI, files, env vars, etc. +For example it can be used to setup a logger as shown in the `obsolete` [pragma +doc](#configuration-field-pragmas). + +```nim +type MyConf = object + # options + +proc onLoaded(config: var MyConf) {.gcsafe, raises: [ConfigurationError].} = + # Modify the config or do something else + discard + +let c = MyConf.load(onLoaded = onLoaded) +``` + ## Compile-time options #### `confutilsNoColors` diff --git a/confutils.nim b/confutils.nim index dd57d5c..49b0c33 100644 --- a/confutils.nim +++ b/confutils.nim @@ -1117,8 +1117,8 @@ proc loadImpl[C, SecondarySources]( ) {.gcsafe, raises: [ConfigurationError].} = nil, envVarsPrefix = appInvocation(), termWidth = 0, - loggerSetup: proc ( - config: Configuration + onLoaded: proc ( + config: var Configuration ) {.gcsafe, raises: [ConfigurationError].} = nil, ): Configuration {.raises: [ConfigurationError].} = ## Loads a program configuration by parsing command-line arguments @@ -1398,9 +1398,9 @@ proc loadImpl[C, SecondarySources]( for cmd in activeCmds: result.processMissingOpts(cmd) - if not isNil(loggerSetup): + if not isNil(onLoaded): try: - loggerSetup(result) + onLoaded(result) except ConfigurationError as err: fail "Failed to setup the logger: '" & err.msg & "'" @@ -1420,13 +1420,13 @@ template load*( secondarySources: untyped = nil, envVarsPrefix = appInvocation(), termWidth = 0, - loggerSetup: untyped = nil + onLoaded: untyped = nil ): untyped = block: let secondarySourcesRef = generateSecondarySources(Configuration) loadImpl(Configuration, cmdLine, version, copyrightBanner, printUsage, quitOnFailure, ignoreUnknown, - secondarySourcesRef, secondarySources, envVarsPrefix, termWidth, loggerSetup) + secondarySourcesRef, secondarySources, envVarsPrefix, termWidth, onLoaded) func defaults*(Configuration: type): Configuration = load(Configuration, cmdLine = @[], printUsage = false, quitOnFailure = false) diff --git a/tests/test_all.nim b/tests/test_all.nim index 5dd5da3..c403b18 100644 --- a/tests/test_all.nim +++ b/tests/test_all.nim @@ -18,6 +18,7 @@ import test_nested_cmd, test_obsolete_overload, test_obsolete, + test_onloaded, test_parsecmdarg, test_pragma, test_qualified_ident, diff --git a/tests/test_obsolete.nim b/tests/test_obsolete.nim index 8c8a9f8..1338282 100644 --- a/tests/test_obsolete.nim +++ b/tests/test_obsolete.nim @@ -82,17 +82,17 @@ suite "test obsolete option overload": check registry == @["opt3", "opt3 obsolete msg"] test "the logger setup is called": - proc loggerSetup(c: OverloadConf) = + proc onLoaded(c: var OverloadConf) = doAssert c.opt1 == "opt1 default" registry.add "logger" registry.setLen 0 - let conf = OverloadConf.load(loggerSetup = loggerSetup) + let conf = OverloadConf.load(onLoaded = onLoaded) check conf.opt1 == "opt1 default" check registry == @["logger"] test "the logger setup is called before the overload": - proc loggerSetup(c: OverloadConf) = + proc onLoaded(c: var OverloadConf) = doAssert c.opt1 == "foo" registry.add "logger" @@ -101,7 +101,7 @@ suite "test obsolete option overload": cmdLine = @[ "--opt1=foo" ], - loggerSetup = loggerSetup + onLoaded = onLoaded ) check conf.opt1 == "foo" check registry == @["logger", "opt1"] diff --git a/tests/test_onloaded.nim b/tests/test_onloaded.nim new file mode 100644 index 0000000..fda3347 --- /dev/null +++ b/tests/test_onloaded.nim @@ -0,0 +1,49 @@ +import + unittest2, + ../confutils + +type + TestConf = object + opt1 {. + defaultValue: "opt1 default" + name: "opt1"}: string + +var registry {.threadvar.}: seq[string] + +suite "test onLoaded parameter": + test "the onloaded callback is called": + proc onLoaded(c: var TestConf) = + doAssert c.opt1 == "opt1 default" + registry.add "called" + + registry.setLen 0 + let conf = TestConf.load(onLoaded = onLoaded) + check conf.opt1 == "opt1 default" + check registry == @["called"] + + test "modify the config var": + proc onLoaded(c: var TestConf) = + doAssert c.opt1 == "foo" + c.opt1 = "foo modified" + registry.add "modified" + + registry.setLen 0 + let conf = TestConf.load( + cmdLine = @[ + "--opt1=foo" + ], + onLoaded = onLoaded + ) + check conf.opt1 == "foo modified" + check registry == @["modified"] + + test "use a callback closure": + var message = "closure" + proc onLoaded(c: var TestConf) {.closure.} = + doAssert c.opt1 == "opt1 default" + registry.add message + + registry.setLen 0 + let conf = TestConf.load(onLoaded = onLoaded) + check conf.opt1 == "opt1 default" + check registry == @["closure"]