#configuration #compile-time #notifications #system #field #modification

no-std snec

编译时字段查找和修改通知的配置系统

2 个版本 (1 个稳定版)

1.0.0 2020年11月8日
0.1.0 2020年8月26日

#310 in 配置

MIT/Apache

40KB
468

Snec

Crates.io Docs.rs Build Status

编译时字段查找和修改通知的配置系统。

概述

Snec 是一个专注于编译时保证和通知运行系统配置值更改方式的配置系统。它的大部分功能是通过宏实现的,这也是为什么默认导出宏的原因。

虽然不提供内置的序列化支持,但 Snec 的架构本身是序列化无关的——使用 Serde 和 Snec 对同一配置表结构进行序列化将正常工作。

Snec 的架构由以下关键组件组成

  • 配置表 — 包含程序配置数据的结构。配置表实现 Get 特性以访问其字段,这使得它们可以向字段提供 Handle。处理程序确保在字段更改时通知指定的接收器,除非明确提示执行静默修改。
  • 条目 — 实现 Entry 特性的一个空类型(没有可能值的类型),代表配置表内部字段的标识符。
  • 接收器 — 实现 Receiver 特性的类型,在它感兴趣的配置表条目被修改时将接收通知。

基本示例

use snec::{ConfigTable, Entry, GetExt as _};
use std::time::{SystemTime, Duration};
#[derive(ConfigTable)]
struct MyConfigTable {
    #[snec]
    when: SystemTime,
    #[snec]
    who: String,
    #[snec]
    in_which_country: String,
}
let mut config_table = MyConfigTable {
    when: SystemTime::UNIX_EPOCH + Duration::from_secs(566_200_800),
    who: "Jeremy".to_string(),
    in_which_country: "USA".to_string(),
};
// 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.get_handle_to::<entries::InWhichCountry>();
// After we got the handle, we can use it to get a
// mutable reference to the field and modify it:
{
    let mut in_which_country = handle.modify();
    *in_which_country = "Britain".to_string();
}
// 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:
{
    let in_which_country = handle.modify_silently();
    *in_which_country = "Australia".to_string();
}

使用接收器

use snec::{ConfigTable, Receiver, Entry, GetExt as _};
use std::time::{SystemTime, Duration};
#[derive(ConfigTable)]
#[snec(
    // Any expression can be used in the braces. After the colon, the type is supplied.
    receiver({MyReceiver}: MyReceiver)
)]
struct MyConfigTable {
    #[snec]
    which_year: i64,
    #[snec(entry, receiver({snec::EmptyReceiver}: snec::EmptyReceiver))]
    why: String,
    #[snec]
    random_integer_that_i_like: u128,
}

struct MyReceiver;
impl Receiver<entries::RandomIntegerThatILike> for MyReceiver {
    fn receive(&mut self, new_value: &u128) {
        println!("My integer has been changed to {}!!", new_value)
    }
}
impl Receiver<entries::WhichYear> for MyReceiver {
    fn receive(&mut self, new_value: &i64) {
        println!("Resceduled to {}", new_value)
    }
}

let mut config_table = MyConfigTable {
    which_year: 1987,
    why: "Accident".to_string(),
    random_integer_that_i_like: 687_800,
};
// Now we have receivers which will immediately react to any changes in the values:
let mut handle = config_table.get_handle_to::<entries::WhichYear>();
{
    let mut which_year = handle.modify();
    *which_year = 1983;
}
// When the scope ends, the `which_year` guard is dropped and the receiver is informed.

依赖项

~225KB