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}