config_it/lib.rs
1//!
2//! A crate for asynchronous centralized configuration management.
3//!
4//! # Usage
5//!
6//! You can define 'Template' which defines set of grouped properties, which should be updated at
7//! once. Only `config_it` decorated properties will be counted as part of given group and will be
8//! managed.
9//!
10//! `Template` must implement `Clone` trait.
11//!
12//! You should create `Storage` to create config group instances(`Group<T:Template>`). `Storage` is
13//! the primary actor for centralized configuration management. Config groups can be instantiated
14//! based on storage instance.
15//!
16//! Any property implements `serde::Serialize`, `serde::DeserializeOwned` can be used as
17//! configuration property.
18//!
19//! # Attributes
20//!
21//! Any field decorated with attribute `#[config_it]` or `#[config]` will be treated as
22//! configuration property. You can specify additional constraints for the property by adding
23//! additional attributes inside parenthesis.
24//!
25//! - `default = <value>`
26//! - Specify default value for the property. as the <value> expression is converted into field
27//! type using `.try_into().unwrap()` expression, you should specify un-fallible expression
28//! here. This is to support convenient value elevation from `&str` to `String`, or similar
29//! owning conversions.
30//! - `default_expr = "<expr>"`
31//! - Specify complicated value expression here. To specify string literal here, you have to
32//! escape double quotes(`"`) with backslash(`\`). As this attribute converts given expression
33//! into token tree directly, you can write any valid rust expression here.
34//! - `rename = "<alias>"`
35//! - Specify alias name for the property. This is useful when you want to use different name
36//! for the property in config file, but want to use original name in code.
37//! - `one_of = [<value>, <value>, ...]`
38//! - Specify set of allowed values for the property. This is useful when you want to restrict
39//! the value of the property to specific set of values. You can also specify default value as
40//! one of the allowed values.
41//! - Default value can be out of the allowed set, and can be excluded from the allowed set. In
42//! this case, setting value back to default value will not be allowed.
43//! - `min = <value>`, `max=<value>`
44//! - Constrain the value of the property to be within given range. Any type which implements
45//! `Ord` can have min/max constraints.
46//! - `env = "<env_var>"` or `env_once = "<env_var>"`
47//! - Specify environment variable name to import value from. If the environment variable is not
48//! set, the default value will be used. `TryParse` trait is used to convert environment
49//! variable value into property type.
50//! - `env_once` is only evaluated once lazily and reuse cached value after creation.
51//! - `no_import`
52//! - Do not update value from imported archive. This is useful when mixed with `env` flag,
53//! which will keep its value as imported environment variable even after the archive is
54//! imported.
55//! - `no_export`
56//! - Do not export value to archive.
57//! - `transient`
58//! - Value won't be archived, and won't be imported from archive.
59//! - `hidden`
60//! - Hints to monitoring system that this property should not be visible.
61//! - `no_notify`
62//!
63//! # Non-default values
64//!
65//! A storage template may consist of multiple non-config field. Since [`Template`] macro does not
66//! require `Default` trait to be implemented, it will report error if the non-config type does not
67//! provide type-level default implementation.
68//!
69//! ```compile_fail
70//! #[derive(config_it::Template, Clone)]
71//! struct Config {
72//! #[config(default = 154)]
73//! pub any_number: i32,
74//!
75//! // This is just okay.
76//! pub non_config_with_default: usize,
77//!
78//! pub non_config_number: std::num::NonZeroUsize,
79//! // ^^^ the trait `Default` is not implemented for `NonZeroUsize`
80//! }
81//! ```
82//! In this case, you can specify non-default value for the field via `non_config_default_expr`
83//! attribute. This attribute accepts string literal, which will be parsed as rust expression.
84//!
85//! ```no_run
86//! #[derive(config_it::Template, Clone)]
87//! struct Config {
88//! #[config(default = 154)]
89//! pub any_number: i32,
90//!
91//! #[non_config_default_expr = r#"1.try_into().unwrap()"#]
92//! pub non_config_number: std::num::NonZeroUsize,
93//! }
94//! ```
95//!
96#[cfg(feature = "config")]
97pub mod config;
98pub mod shared;
99
100// Just re-exported, for compatibility.
101pub extern crate serde;
102
103pub use shared::{archive, meta};
104
105// [`core::archive::Archive`] utilizes this.
106pub use serde_json::Value as ArchiveValue;
107
108#[cfg(feature = "jsonschema")]
109pub extern crate schemars;
110
111#[cfg(feature = "jsonschema")]
112pub use schemars::schema::{RootSchema as Schema, SchemaObject};
113
114#[cfg(feature = "config")]
115pub use config_export::*;
116
117#[doc(hidden)]
118#[cfg(feature = "config-derive")]
119pub use memoffset::offset_of;
120
121#[doc(hidden)]
122#[cfg(feature = "config-derive")]
123pub use impls::impls;
124
125/// Primary macro for defining configuration group template.
126#[cfg(feature = "config-derive")]
127pub use macros::Template;
128
129#[cfg(feature = "config")]
130mod config_export {
131 use crate::config::*;
132 use crate::shared::*;
133
134 pub use entity::{Validation, ValidationResult};
135
136 pub use archive::Archive;
137 pub use group::{Group, Template};
138 pub use storage::{Monitor, Storage};
139
140 #[cfg(feature = "arc-swap")]
141 pub use storage::atomic::AtomicStorageArc;
142
143 pub fn create_storage() -> Storage {
144 Storage::default()
145 }
146
147 pub type BroadcastReceiver = noti::Receiver;
148 pub type ArchiveCategoryRule<'a> = archive::CategoryRule<'a>;
149
150 /// Shorthand macro for consuming multiple updates.
151 ///
152 /// With bracket syntax, this macro returns array of bool values, which indicates whether each
153 /// elements has been updated. With simple variadic syntax, this macro returns boolean value which
154 /// indicates whether any of given elements has been updated.
155 ///
156 /// If elements are wrapped inside parenthesis, this macro returns temporary struct which has
157 /// boolean fields names each elements.
158 ///
159 /// In both cases, all supplied arguments will be evaluated.
160 #[macro_export]
161 macro_rules! consume_update{
162 ($group: expr, [$($elems:ident),*]) => {
163 {
164 let __group = ($group).__macro_as_mut();
165 [$(__group.consume_update(&__group.$elems)), *]
166 }
167 };
168
169 ($group: expr, (($($elems:ident),*))) => {
170 {
171 #[derive(Debug, Clone, Copy)]
172 struct Updates {
173 $($elems: bool),*
174 }
175
176 let __group = ($group).__macro_as_mut();
177 Updates {
178 $($elems: __group.consume_update(&__group.$elems)),*
179 }
180 }
181 };
182
183 ($group: expr, $($elems:ident),*) => {
184 {
185 let __group = ($group).__macro_as_mut();
186 $(__group.consume_update(&__group.$elems)) | *
187 }
188 };
189 }
190
191 /// Shorthand macro for marking multiple elements dirty.
192 #[macro_export]
193 macro_rules! mark_dirty{
194 ($group: expr, $($elems:ident),+) => {
195 {
196 let __group = ($group).__macro_as_mut();
197 $(__group.mark_dirty(&__group.$elems)); *
198 }
199 }
200 }
201
202 /// Shorthand macro for committing multiple elements.
203 #[macro_export]
204 macro_rules! commit_elem{
205 ($group: expr, ($($elems:ident),+)) => {
206 {
207 let __group = ($group).__macro_as_mut();
208 $(__group.commit_elem(&__group.$elems, false)); *
209 }
210 };
211
212 ($group: expr, notify($($elems:ident),+)) => {
213 {
214 let __group = ($group).__macro_as_mut();
215 $(__group.commit_elem(&__group.$elems, true)); *
216 }
217 };
218 }
219}