config-it
Asynchronous Centralized Configuration Management for Rust
config-it is an asynchronous library that offers centralized configuration management for Rust applications. Here are its key features:
- Define custom configuration templates using structs.
- Create multiple instances with different paths based on a single template.
- Receive notifications when your instance is updated.
- Use
serde compatible archive formats to archive your data in your preferred way.
- Manage property-wise dirty flags for precise, responsive updates to your system.
As I'm not very good at writing English sentences, got some help from AI to write this README file. Thanks, robot!
Usage
#[derive(config_it::Template, Clone, Default)]
struct MyConfig {
#[config_it]
string_field: String,
#[config_it(default = 3, min = 1, max = 5)]
int_field: i32,
#[config_it(alias = "alias")]
non_alias: f32,
#[config_it(default = "default", one_of("a", "b", "c"))]
one_of_field: String,
#[config_it]
c_string_type: Box<std::ffi::CStr>,
#[config_it(env = "MY_ARRAY_VAR")]
env_var: i64,
#[config_it(default_expr = "[1,2,3,4,5].into()")]
array_init: Vec<i32>,
_not_part_of: (),
#[config_it(no_import, no_export)]
no_imp_exp: Vec<f64>,
#[config_it(transient)]
no_imp_exp_2: Vec<f64>,
}
let (storage, driver_task) = config_it::create_storage();
let mut local = futures::executor::LocalPool::new();
let spawn = local.spawner();
use futures::task::SpawnExt;
spawn.spawn(driver_task).unwrap();
std::env::set_var("MY_ARRAY_VAR", "123");
local.run_until(async {
let path = &["path", "to", "my", "group"];
let mut group = storage.create_group::<MyConfig>(path).await.unwrap();
assert!(storage.create_group::<MyConfig>(path).await.is_err());
assert!(group.update() == true);
assert!(group.update() == false);
assert!(true == group.check_elem_update(&group.array_init));
assert!(true == group.check_elem_update(&group.c_string_type));
assert!(true == group.check_elem_update(&group.env_var));
assert!(true == group.check_elem_update(&group.no_imp_exp));
assert!(true == group.check_elem_update(&group.no_imp_exp_2));
assert!(true == group.check_elem_update(&group.non_alias));
assert!(true == group.check_elem_update(&group.int_field));
assert!(true == group.check_elem_update(&group.one_of_field));
assert!(true == group.check_elem_update(&group.string_field));
assert!(false == group.check_elem_update(&group.array_init));
assert!(false == group.check_elem_update(&group.c_string_type));
assert!(false == group.check_elem_update(&group.env_var));
assert!(false == group.check_elem_update(&group.no_imp_exp));
assert!(false == group.check_elem_update(&group.no_imp_exp_2));
assert!(false == group.check_elem_update(&group.int_field));
assert!(false == group.check_elem_update(&group.non_alias));
assert!(false == group.check_elem_update(&group.one_of_field));
assert!(false == group.check_elem_update(&group.string_field));
assert!(group.string_field == "");
assert!(group.array_init == &[1, 2, 3, 4, 5]);
assert!(group.env_var == 123);
let archive = storage.export(Default::default()).await.unwrap();
let yaml = serde_yaml::to_string(&archive).unwrap();
let json = serde_json::to_string_pretty(&archive).unwrap();
println!("{}", yaml);
println!("{}", json);
let yaml = r##"
~path:
~to:
~my:
~group:
alias: 3.14
array_init:
- 1
- 145
int_field: 3 # If there's no change, it won't be updated.
# This behavior can be overridden by import options.
env_var: 59
one_of_field: "hello" # This is not in the 'one_of' list...
"##;
let archive: config_it::Archive = serde_yaml::from_str(yaml).unwrap();
storage.import(archive, Default::default()).await.unwrap();
storage.fence().await;
assert!(group.update() == true);
assert!(group.non_alias == 3.14); assert!(group.array_init == [1, 145]);
assert!(group.env_var == 59);
assert!(group.int_field == 3); assert!(group.one_of_field == "default");
assert!(true == group.check_elem_update(&group.non_alias));
assert!(true == group.check_elem_update(&group.array_init));
assert!(true == group.check_elem_update(&group.env_var));
assert!(false == group.check_elem_update(&group.int_field));
assert!(false == group.check_elem_update(&group.one_of_field));
assert!(false == group.check_elem_update(&group.c_string_type));
assert!(false == group.check_elem_update(&group.no_imp_exp));
assert!(false == group.check_elem_update(&group.no_imp_exp_2));
assert!(false == group.check_elem_update(&group.string_field));
let mut monitor = group.watch_update();
assert!(false == monitor.try_recv().is_ok());
let archive: config_it::Archive = serde_yaml::from_str(yaml).unwrap();
storage
.import(
archive,
config_it::ImportOptions {
apply_as_patch: false, ..Default::default()
},
)
.await
.unwrap();
assert!(true == monitor.recv().await.is_ok());
assert!(group.update());
group.int_field = 15111;
group.commit_elem(&group.int_field, false);
let archive = storage.export(Default::default()).await.unwrap();
assert!(
archive.find_path(path.iter().map(|x| *x)).unwrap().values["int_field"]
.as_i64()
.unwrap()
== 15111
);
storage
.import(
archive,
config_it::ImportOptions {
apply_as_patch: false,
..Default::default()
},
)
.await
.unwrap();
storage.fence().await;
assert!(group.update());
assert!(group.int_field == 5);
let _ch = storage.monitor_open_replication_channel().await;
});