Skip to content

feat: improved config validation#2325

Open
aclauer wants to merge 6 commits into
mainfrom
andrew/feat/better-config-validation
Open

feat: improved config validation#2325
aclauer wants to merge 6 commits into
mainfrom
andrew/feat/better-config-validation

Conversation

@aclauer
Copy link
Copy Markdown
Collaborator

@aclauer aclauer commented Jun 1, 2026

Problem

There was a lot of boiler plate for validating config structs in Rust native modules.

Closes DIM-971

Solution

Add ModuleConfig trait and use validator crate to make it easier to add validation to Config structs. Now you can use the validate attribute on struct fields, either the supported ones from the crate or write your own custom ones. Also can do schema level, which I just learned about.

How to Test

cargo test

Bad configs in the rust_ping_pong.py create error messages like this:

config error message

Contributor License Agreement

  • I have read and approved the CLA.

@aclauer aclauer linked an issue Jun 1, 2026 that may be closed by this pull request
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@aclauer aclauer marked this pull request as ready for review June 1, 2026 23:43
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Jun 1, 2026

Greptile Summary

This PR replaces per-module hand-rolled config validation functions with a declarative ModuleConfig trait backed by the validator crate, and changes run() to return () (handling errors internally via process::exit(1)) rather than io::Result<()>.

  • ModuleConfig + NoConfig: A blanket impl of ModuleConfig for any DeserializeOwned + Debug + Validate type is added; NoConfig (unit struct) replaces () as the generated default when no #[config] field is declared. The #[derive(Module)] macro is updated to emit the new default type.
  • validate_config + format_validation_errors: After JSON deserialization in run_fallible, config.validate() is called and any errors are formatted into a human-readable string; schema-level (__all__) errors are stripped of their internal key prefix before display.
  • Call-site cleanup: All main functions drop the .expect() call on run(); RayTracingVoxelMap's old async validate_config setup method and its #[module(setup = ...)] hook are removed in favour of the new field-level and schema-level #[validate(...)] annotations.

Confidence Score: 5/5

Safe to merge; the change is additive and all existing call sites have been updated.

The core logic in format_validation_errors, validate_config, and the NoConfig unit struct is straightforward and well-tested. The all schema-key stripping is handled correctly in with_field. The only gap is that the null_config_succeeds_for_unit_type test still exercises () rather than NoConfig, leaving the real generated default type untested for null deserialization — but based on serde semantics for unit structs this path is expected to work correctly.

native/rust/dimos-module/src/module.rs — the existing unit-type null deserialization test should be extended or mirrored for NoConfig.

Important Files Changed

Filename Overview
native/rust/dimos-module/src/module.rs Core change: adds ModuleConfig trait, NoConfig unit struct, format_validation_errors helper, and validate_config fn; changes run() to return () and call process::exit(1) on failure. Logic is sound; one test coverage gap with NoConfig deserialization from null.
native/rust/dimos-module-macros/src/lib.rs Default config type changed from () to ::dimos_module::NoConfig when no #[config] field is present. Simple, correct substitution.
dimos/mapping/ray_tracing/rust/src/main.rs Migrated Config to use #[derive(Validate)] with field-level range constraints and a schema-level cross-field validator; removed the old hand-rolled validate_config setup method.
native/rust/dimos-module/src/lib.rs Re-exports ModuleConfig and NoConfig so consumers don't need to reach into the module internals.
examples/native-modules/rust/src/native_pong.rs Adds #[derive(Validate)] and a range constraint to PongConfig; updates run() call site to drop the now-removed Result return.
native/rust/README.md Documentation updated to reflect Validate requirement on config types and the new config validation section with schema-level example.

Sequence Diagram

sequenceDiagram
    participant Coordinator as Python Coordinator
    participant run as run()
    participant run_f as run_fallible()
    participant parse as parse_config_json()
    participant validate as validate_config()
    participant module as Module

    Coordinator->>run: spawn process, write JSON to stdin
    run->>run_f: delegate
    run_f->>run_f: init_tracing()
    run_f->>parse: read stdin, deserialize
    parse-->>run_f: topics + config
    run_f->>validate: config.validate()
    alt validation fails
        validate-->>run_f: Err(ValidationErrors)
        run_f-->>run: Err(io::Error)
        run->>run: log error, process::exit(1)
    else validation passes
        validate-->>run_f: Ok(())
        run_f->>module: build + setup + handle + teardown
        module-->>run_f: done
        run_f-->>run: Ok(())
    end
Loading

Reviews (2): Last reviewed commit: "Merge branch 'main' into andrew/feat/bet..." | Re-trigger Greptile

Comment thread native/rust/dimos-module/src/module.rs Outdated
Comment thread native/rust/dimos-module/src/module.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Better config validation in Rust modules

1 participant