Configuration system with compile-time field lookup and modification notifications.
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.
```rust use snec::{ConfigTable, Entry, GetExt as _}; use std::time::{SystemTime, Duration};
struct MyConfigTable {
#[snec]
when: SystemTime,
#[snec]
who: String,
#[snec]
inwhichcountry: String,
}
let mut configtable = MyConfigTable {
when: SystemTime::UNIXEPOCH + Duration::fromsecs(566200800),
who: "Jeremy".tostring(),
inwhichcountry: "USA".tostring(),
};
// To access the fields of our config table, we need to use the gethandle 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 = configtable.gethandleto::
Using receivers:
rust
use snec::{ConfigTable, Receiver, Entry, GetExt as _};
use std::time::{SystemTime, Duration};
// Any expression can be used in the braces. After the colon, the type is supplied.
receiver({MyReceiver}: MyReceiver)
)] struct MyConfigTable { #[snec] whichyear: i64, #[snec(entry, receiver({snec::EmptyReceiver}: snec::EmptyReceiver))] why: String, #[snec] randomintegerthati_like: u128, }
struct MyReceiver;
impl Receiver
let mut configtable = MyConfigTable {
whichyear: 1987,
why: "Accident".tostring(),
randomintegerthatilike: 687800,
};
// Now we have receivers which will immediately react to any changes in the values:
let mut handle = configtable.gethandleto::which_year
guard is dropped and the receiver is informed.
```