Modules can report why they failed

A module had one channel back to its caller: the int returned from
dpm_module_execute, handed through by dpm_execute. Any detail behind
that number could only reach a log, so a caller wanting the reason had
to read output rather than ask for it.

dpm_set_last_error joins the exported API. A module records its reason
on the context handed to its entry point, which belongs to the caller,
and the caller reads it back with dpm_get_last_error.

The two accessors on the context now say what they do to it:
dpm_module_path becomes dpm_get_resolved_module_path, naming the value
it reports rather than the setting it came from, and dpm_last_error
becomes dpm_get_last_error, pairing with the setter.

Path normalization moves to sanitizers.cpp, which holds the conversions
that put a value written by a person into the single form the library
stores it in.
This commit is contained in:
2026-08-19 00:49:42 -04:00
parent c71913ded9
commit 50d71ca55f
14 changed files with 162 additions and 58 deletions

View File

@@ -92,10 +92,13 @@ Returns the configured value for `key` in `section` of the named module's config
`void dpm_log(dpm_ctx* ctx, int level, const char* message)`
Writes `message` at `level` (FATAL=0, ERROR=1, WARN=2, INFO=3, DEBUG=4) to the context's configured log targets (console and/or file). Messages above the configured level are dropped. NULL message is a no-op.
`const char* dpm_module_path(dpm_ctx* ctx)`
`const char* dpm_get_resolved_module_path(dpm_ctx* ctx)`
Returns the resolved module directory path for this context.
`const char* dpm_last_error(dpm_ctx* ctx)`
`void dpm_set_last_error(dpm_ctx* ctx, const char* msg)`
Records `msg` as the most recent failure on this context; the string is copied. A module explains a nonzero return by recording the reason here, using the context handed to its entry point — the caller's own — so that the caller reads back what the module wrote. The context holds one reason at a time.
`const char* dpm_get_last_error(dpm_ctx* ctx)`
Returns a human-readable description of the most recent failure recorded on this context, or NULL if none. Overwritten by the next failing call on the same context.
## Module Contract
@@ -103,7 +106,7 @@ Returns a human-readable description of the most recent failure recorded on this
A module is one .so in the module directory. It includes `<dpm/core.h>`, links `-ldpm-core`, and exports the following reserved symbols as extern "C". Returned strings are static or module-owned, non-NULL, and valid for the lifetime of the loaded module; the library and consumers never free them.
`int dpm_module_execute(dpm_ctx* ctx, const char* command, int argc, char** argv)`
The module's command entry point, and the only entry through which it performs work. `ctx` is the host context that dispatched the call — the module reaches every service (`dpm_log`, `dpm_config_get`, `dpm_module_path`, ...) through it, and reaches peer modules through it as well. `command` is the subcommand name (equal to argv[0] when argc > 0); argc/argv are the remaining CLI-style arguments. NULL or empty `command` must behave as the module's help command. Returns 0 on success, nonzero on failure. It must be callable immediately after load with no other setup.
The module's command entry point, and the only entry through which it performs work. `ctx` is the host context that dispatched the call — the module reaches every service (`dpm_log`, `dpm_config_get`, `dpm_get_resolved_module_path`, ...) through it, and reaches peer modules through it as well. `command` is the subcommand name (equal to argv[0] when argc > 0); argc/argv are the remaining CLI-style arguments. NULL or empty `command` must behave as the module's help command. Returns 0 on success, nonzero on failure. It must be callable immediately after load with no other setup.
`const char* dpm_module_version(void)`
Returns the module's own version as an X.Y.Z string. Must be constant for the life of the module. This is the value the library reports to consumers, and the value they judge compatibility against.