From 28521240176cd1fbcc025169324aa9d0f24bbbbd Mon Sep 17 00:00:00 2001 From: nitely Date: Mon, 12 Jan 2026 14:33:06 -0300 Subject: [PATCH 1/3] Followup #136; change loggerSetup to onLoaded --- README.md | 24 +++++++++++++++++++++--- confutils.nim | 12 ++++++------ tests/test_obsolete.nim | 8 ++++---- 3 files changed, 31 insertions(+), 13 deletions(-) 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_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"] From e286cddb44fda4d08041f312341fef32ba8c7517 Mon Sep 17 00:00:00 2001 From: nitely Date: Mon, 12 Jan 2026 14:43:18 -0300 Subject: [PATCH 2/3] test --- tests/test_all.nim | 1 + tests/test_onloaded.nim | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/test_onloaded.nim 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_onloaded.nim b/tests/test_onloaded.nim new file mode 100644 index 0000000..fe0c660 --- /dev/null +++ b/tests/test_onloaded.nim @@ -0,0 +1,38 @@ +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"] From 03fe83d9fc76b24ea329d3fa46231f77e45fbac7 Mon Sep 17 00:00:00 2001 From: nitely Date: Mon, 12 Jan 2026 14:53:44 -0300 Subject: [PATCH 3/3] test --- tests/test_onloaded.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_onloaded.nim b/tests/test_onloaded.nim index fe0c660..fda3347 100644 --- a/tests/test_onloaded.nim +++ b/tests/test_onloaded.nim @@ -36,3 +36,14 @@ suite "test onLoaded parameter": ) 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"]