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
//!
//! A crate for asynchronous centralized configuration management.
//!
//! # Usage
//!
//! You can define 'ConfigGroupData' 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.
//!
//! `ConfigGroupData` must implement `Clone` and `Default` to be treated as valid config
//!  group data.
//!
//! You should create `Storage` to create config group instances(`Group<T:ConfigGroupData>`).
//!  `Storage` is the primary actor for centralized configuration management. Config groups can
//!  be instantiated based on storage instance.
//!
//! # Example usage
//!
//! ```
//! use futures::executor::{self, block_on};
//!
//! ///
//! /// Define set of properties using derive macro
//! ///
//! /// `Debug` implementation is optional.
//! ///
//! #[derive(Clone, config_it::ConfigGroupData, Default, Debug)]
//! pub struct MyStruct {
//!     /// This docstring will be contained as metadata of given element
//!     #[config_it(min = -35)]
//!     minimal: i32,
//!
//!     #[config_it(default = 2, max = 3)]
//!     maximum: i32,
//!
//!     #[config_it(default = "3@", one_of("ab3", "go04"))]
//!     data: String,
//!
//!     #[config_it(default = 3112, one_of(1, 2, 3, 4, 5))]
//!     median: i32,
//!
//!     /// This property won't be counted as config group's property
//!     transient: f32,
//! }
//!
//! // Second tuple parameter is 'actor', which asynchronously drives all multithreaded
//! //  operations safely. If you utilize async executors, for example, `tokio`, then you can
//! //  simply spawn the whole storage operations as light-weight async task.
//! let (storage, async_worker) = config_it::Storage::new();
//! std::thread::spawn(move || block_on(async_worker)); // tokio::task::spawn(async_worker)
//!
//! // Since most of storage operations are run through actor, it is usually recommended to
//! //  use config_it inside async context.
//! let job = async move {
//!     // You can instantiate group within storage with `prefix` words.
//!     // Each words indicates hierarchy.
//!     let mut group: config_it::Group<_> =
//!         storage.create_group::<MyStruct>(["Main"]).await.unwrap();
//!
//!     // "Main" - `group`
//!     //   +-> "Sub" - `subgroup`
//!     let subgroup = storage
//!         .create_group::<MyStruct>(["Main", "Sub"])
//!         .await
//!         .unwrap();
//!
//!     // Prefix duplication is not allowed.
//!     storage
//!         .create_group::<MyStruct>(["Main", "Sub"])
//!         .await
//!         .unwrap_err();
//!
//!     // Group can fetch and apply changes from storage, using `update` method.
//!     // This operation clears dirty flag of config group, thus next update call won't
//!     //  return true until any update is committed to storage.
//!     assert!(group.update());
//!     assert!(!group.update());
//!
//!     // Individual properties of config group has their own dirty flag, thus after update,
//!     //  you can check if which element has changed. This operation consumes dirty flag
//!     //  either.
//!     assert!(group.check_elem_update(&group.data));
//!     assert!(!group.check_elem_update(&group.data));
//!
//!     // TODO: Write this ...
//!
//!     // As `Group<T>` implements `Deref` and `DerefMut` traits, you can access its values
//!     //  in simple manner.
//!     assert!(&group.data == "3@");
//!
//!     // You can modify `ConfigGroupData`, however, this change won't be visible to
//!     //  storage until you publish this change.
//!     group.data = "other_value".into();
//!
//!     // You can publish your changes to storage.
//!     // Second boolean parameter indicates whether you want to propagate this change or not.
//!     //  If you check 'true' here, then this change will
//!     group.commit_elem(&group.data, false);
//! };
//!
//! block_on(job);
//!
//! ```
//!
pub mod archive;
pub mod config;
pub mod core;
pub mod entity;
pub mod monitor;
pub mod storage;

pub use smartstring::alias::CompactString;

pub use archive::Archive;
pub use config::ConfigGroupData;
pub use config::Group;
pub use monitor::StorageMonitor;
pub use storage::Storage;

pub use lazy_static::lazy_static;
pub use macros::ConfigGroupData;

mod ttt {
    mod config_it {
        pub use crate::*;
    }

    #[test]
    fn doctest() {
        use futures::executor::{self, block_on};

        ///
        /// Define set of properties using derive macro
        ///
        /// `Debug` implementation is optional.
        ///
        #[derive(Clone, config_it::ConfigGroupData, Default, Debug)]
        pub struct MyStruct {
            /// This docstring will be contained as metadata of given element
            #[config_it(min = -35)]
            minimal: i32,

            #[config_it(default = 2, max = 3)]
            maximum: i32,

            #[config_it(default = "3@", one_of("ab3", "go04"))]
            data: String,

            #[config_it(default = 3112, one_of(1, 2, 3, 4, 5))]
            median: i32,

            /// This property won't be counted as config group's property
            transient: f32,
        }

        // Second tuple parameter is 'actor', which asynchronously drives all multithreaded
        //  operations safely. If you utilize async executors, for example, `tokio`, then you can
        //  simply spawn the whole storage operations as light-weight async task.
        let (storage, async_worker) = config_it::Storage::new();
        std::thread::spawn(move || block_on(async_worker)); // tokio::task::spawn(async_worker)

        // Since most of storage operations are run through actor, it is usually recommended to
        //  use config_it inside async context.
        let job = async move {
            // You can instantiate group within storage with `prefix` words.
            // Each words indicates hierarchy.
            let mut group: config_it::Group<_> =
                storage.create_group::<MyStruct>(["Main"]).await.unwrap();

            // "Main" - `group`
            //   +-> "Sub" - `subgroup`
            let subgroup = storage
                .create_group::<MyStruct>(["Main", "Sub"])
                .await
                .unwrap();

            // Prefix duplication is not allowed.
            storage
                .create_group::<MyStruct>(["Main", "Sub"])
                .await
                .unwrap_err();

            // Group can fetch and apply changes from storage, using `update` method.
            // This operation clears dirty flag of config group, thus next update call won't
            //  return true until any update is committed to storage.
            assert!(group.update());
            assert!(!group.update());

            // Individual properties of config group has their own dirty flag, thus after update,
            //  you can check if which element has changed. This operation consumes dirty flag
            //  either.
            assert!(group.check_elem_update(&group.data));
            assert!(!group.check_elem_update(&group.data));

            // TODO: Write this ...

            // As `Group<T>` implements `Deref` and `DerefMut` traits, you can access its values
            //  in simple manner.
            assert!(&group.data == "3@");

            // You can modify `ConfigGroupData`, however, this change won't be visible to
            //  storage until you publish this change.
            group.data = "other_value".into();

            // You can publish your changes to storage.
            // Second boolean parameter indicates whether you want to propagate this change or not.
            //  If you check 'true' here, then this change will
            group.commit_elem(&group.data, false);
        };

        block_on(job);
    }
}