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

// Just re-exported, for compatibility.
pub extern crate compact_str;
pub extern crate serde;

pub use shared::{archive, meta};

// [`core::archive::Archive`] utilizes this.
pub use serde_json::Value as ArchiveValue;

#[cfg(feature = "jsonschema")]
pub extern crate schemars;

#[cfg(feature = "jsonschema")]
pub use schemars::schema::{RootSchema as Schema, SchemaObject};

#[cfg(feature = "config")]
pub use config_export::*;

#[doc(hidden)]
#[cfg(feature = "config")]
pub use memoffset::offset_of;

#[doc(hidden)]
#[cfg(feature = "config")]
pub use impls::impls;

#[cfg(feature = "config")]
mod config_export {
    use crate::config::*;
    use crate::shared::*;

    /// Primary macro for defining configuration group template.
    #[cfg(feature = "config")]
    pub use macros::Template;

    pub use entity::{Validation, ValidationResult};

    pub use archive::Archive;
    pub use group::{Group, Template};
    pub use storage::Storage;

    pub fn create_storage() -> Storage {
        Storage::default()
    }

    pub type BroadcastReceiver = noti::Receiver;
    pub type ArchiveCategoryRule<'a> = archive::CategoryRule<'a>;

    /// Shorthand macro for consuming multiple updates.
    ///
    /// With bracket syntax, this macro returns array of bool values, which indicates whether each
    /// elements has been updated. With simple variadic syntax, this macro returns boolean value which
    /// indicates whether any of given elements has been updated.
    ///
    /// If elements are wrapped inside parenthesis, this macro returns temporary struct which has
    /// boolean fields names each elements.
    ///
    /// In both cases, all supplied arguments will be evaluated.
    #[macro_export]
    macro_rules! consume_update{
        ($group: expr, [$($elems:ident),*]) => {
            {
                let __group = ($group).__macro_as_mut();
                [$(__group.consume_update(&__group.$elems)), *]
            }
        };

        ($group: expr, (($($elems:ident),*))) => {
            {
                #[derive(Debug, Clone, Copy)]
                struct Updates {
                    $($elems: bool),*
                }

                let __group = ($group).__macro_as_mut();
                Updates {
                    $($elems: __group.consume_update(&__group.$elems)),*
                }
            }
        };

        ($group: expr, $($elems:ident),*) => {
            {
                let __group = ($group).__macro_as_mut();
                $(__group.consume_update(&__group.$elems)) | *
            }
        };
    }

    /// Shorthand macro for marking multiple elements dirty.
    #[macro_export]
    macro_rules! mark_dirty{
    ($group: expr, $($elems:ident),+) => {
            {
                let __group = ($group).__macro_as_mut();
                $(__group.mark_dirty(&__group.$elems)); *
            }
        }
    }

    /// Shorthand macro for committing multiple elements.
    #[macro_export]
    macro_rules! commit_elem{
        ($group: expr, ($($elems:ident),+)) => {
            {
                let __group = ($group).__macro_as_mut();
                $(__group.commit_elem(&__group.$elems, false)); *
            }
        };

    ($group: expr, notify($($elems:ident),+)) => {
            {
                let __group = ($group).__macro_as_mut();
                $(__group.commit_elem(&__group.$elems, true)); *
            }
        };
    }
}