figen 0.2.0

Strongly typed configuration bindings and registries generated from a declarative schema
Documentation

figen

figen is a Rust library for generating strongly-typed configuration bindings and registries from a compact declarative schema.

It is designed to simplify configuration management, primarily for embedded development, where parsing and wiring configuration keys by hand becomes repetitive and error-prone.

Why this project exists

Embedded firmware often needs:

  • Static, known-at-compile-time configuration structures
  • Reliable parsing from key-value sources (files, NVS, RPC backends, etc.)
  • A machine-readable registry that clients can fetch to discover capabilities

figen reduces boilerplate by generating the config structs, binders, defaults, and registry metadata from a single source of truth.

Key features

  • Works in both std and no_std environments
  • Declarative config schema via config_registry!
  • Generated strongly-typed config structs
  • Default values and optional fields
  • Nested keys and indexed-array key support
  • Generated registry metadata with versioning
  • Custom property types via TryFrom<&str> + #[derive(ConfigBinder)]

Quick example

use figen::config_registry;

config_registry!(
    name = AppConfig
    version = 1

    str_property("telemetry.endpoint", default = "udp://127.0.0.1:14550", max_len = 64)
    num_property("control.pid.kp", default = 10)
    num_property("control.pid.ki", default = 2)
    bool_property("control.enabled", default = true)
);

// Generated:
// - AppConfig (root config struct)
// - APP_CONFIG_REGISTRY (registry static)

Loading:

let loader = MyPropertyLoader::new(); // implement figen::loader::PropertyLoader
let cfg: AppConfig = figen::load_config(&loader)?;

Multiple registries (example)

You can define independent registries in separate modules and avoid naming collisions:

mod app {
    use figen::config_registry;

    config_registry!(
        name = AppConfig
        version = 1
        num_property("pid.kp", default = 10)
    );
}

mod board {
    use figen::config_registry;

    config_registry!(
        name = BoardConfig
        version = 1
        num_property("gpio.motor_a.pin", default = 12)
    );
}

// app::APP_CONFIG_REGISTRY
// board::BOARD_CONFIG_REGISTRY

Custom types

use figen::config_registry;

#[derive(figen::ConfigBinder, Default)]
struct Gain(u16);

impl TryFrom<&str> for Gain {
    type Error = &'static str;

    fn try_from(value: &str) -> Result<Self, Self::Error> {
        value.parse::<u16>().map(Gain).map_err(|_| "parse error")
    }
}

config_registry!(
    name = AppConfig
    version = 1
    custom_property("control.gain", default = "42", ty = Gain)
);

Feature flags

  • std: enables std support and lazy static registry for runtime-friendly environments
  • serde: enables serde serialization support for generated registry/config metadata

Common combinations:

  • --no-default-features
  • --no-default-features --features serde
  • --no-default-features --features std
  • --no-default-features --features std --features serde

Project status

Early-stage and evolving. Breaking API changes are still possible while the API is being shaped.

Contributing

Issues and pull requests are welcome. If you propose API changes, include motivation and expected embedded/firmware workflow impact.

License

Licensed under either of:

at your option.