Snec
Configuration system with compile-time field lookup and modification notifications.
Overview
Snec is a configuration system focused on compile-time guarantees and a way of notifying a running system that a configurable value changed. Most of its power is implemented via macros, which is why those are exported by default.
While no built-in serialization support is provided, the architecture by itself is serialization-agnostic — using Serde and Snec for the same config table structure will work just fine.
Snec's architecture consists of those key components:
- Config table — the structure which contains the configuration data for the program. Config tables implement the [
Get
] trait to access its fields, which allows them to hand out [Handle
]s to its fields. Handles ensure that the assigned receiver gets notified when the field changes, unless it's explicitly prompted to perform a silent modification. - Entry — an uninhabited type (type with no possible values) implementing the [
Entry
] trait, representing an identifier for a field inside of a config table. - Receiver — type implementing the [
Receiver
] trait which will receive notifications whenever a entry in a config table it's interested in is modified.
Basic example
use ;
use ;
let mut config_table = MyConfigTable ;
// To access the fields of our config table, we need to use the get_handle method from
// the GetExt trait (which is a nicer way to use the Get trait). The `entries` part is
// a module generated by the `#[derive(ConfigTable)]`. In most cases, it's desirable
// to reexport the contents of the module in a public module with a different name and
// some documentation, or simply in the containing module if you want the entry
// identifiers to be in the same module as the config table.
let mut handle = config_table.;
// After we got the handle, we can use it to get a
// mutable reference to the field and modify it:
// The reason why we put that in a scope and why we had to do this entire two-step process
// is because otherwise we'd implicitly avoid notifying any receivers, which is something
// that we'll look into in the next example. Since we don't have any, it won't really
// hurt if we did this as well:
Using receivers:
use ;
use ;
;
let mut config_table = MyConfigTable ;
// Now we have receivers which will immediately react to any changes in the values:
let mut handle = config_table.;
// When the scope ends, the `which_year` guard is dropped and the receiver is informed.