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
//! Configuration system with compile-time field lookup and modification notifications.
//!
//! # Overview
//! Snec is a configuration system focused on compile-time guarantees and a way of notifying a running system that a configurable value changed. Most of its power is implemented via macros, which is why those are exported by default.
//!
//! While no built-in serialization support is provided, the architecture by itself is serialization-agnostic — using Serde and Snec for the same config table structure will work just fine.
//!
//! Snec's architecture consists of those key components:
//! - **Config table** — the structure which contains the configuration data for the program. Config tables implement the `Get` trait to access its fields, which allows them to hand out `Handle`s to its fields. Handles ensure that the assigned receiver gets notified when the field changes, unless it's explicitly prompted to perform a silent modification.
//! - **Entry** — an uninhabited type (type with no possible values) implementing the `Entry` trait, representing an identifier for a field inside of a config table.
//! - **Receiver** — type implementing the `Receiver` trait which will receive notifications whenever a entry in a config table it's interested in is modified.
//!
//! # Basic example
//! ```
//! use snec::{ConfigTable, Entry, GetExt as _};
//! use std::time::{SystemTime, Duration};
//! #[derive(ConfigTable)]
//! struct MyConfigTable {
//!     #[snec]
//!     when: SystemTime,
//!     #[snec]
//!     who: String,
//!     #[snec]
//!     in_which_country: String,
//! }
//! let mut config_table = MyConfigTable {
//!     when: SystemTime::UNIX_EPOCH + Duration::from_secs(566_200_800),
//!     who: "Jeremy".to_string(),
//!     in_which_country: "USA".to_string(),
//! };
//!
//! // To access the fields of our config table, we need to use the get_handle method from
//! // the GetExt trait (which is a nicer way to use the Get trait). The `entries` part is
//! // a module generated by the `#[derive(ConfigTable)]`. In most cases, it's desirable
//! // to reexport the contents of the module in a public module with a different name and
//! // some documentation, or simply in the containing module if you want the entry
//! // identifiers to be in the same module as the config table.
//! let mut handle = config_table.get_handle_to::<entries::InWhichCountry>();
//! // After we got the handle, we can use it to get a
//! // mutable reference to the field and modify it:
//! {
//!     let mut in_which_country = handle.modify();
//!     *in_which_country = "Britain".to_string();
//! }
//! // The reason why we put that in a scope and why we had to do this entire two-step process
//! // is because otherwise we'd implicitly avoid notifying any receivers, which is something
//! // that we'll look into in the next example. Since we don't have any, it won't really
//! // hurt if we did this as well:
//! {
//!     let in_which_country = handle.modify_silently();
//!     *in_which_country = "Australia".to_string();
//! }
//! ```
//! Using receivers:
//! ```
//! use snec::{ConfigTable, Receiver, Entry, GetExt as _};
//! use std::time::{SystemTime, Duration};
//! #[derive(ConfigTable)]
//! #[snec(
//!     // Any expression can be used in the braces. After the colon, the type is supplied.
//!     receiver({MyReceiver}: MyReceiver)
//! )]
//! struct MyConfigTable {
//!     #[snec]
//!     which_year: i64,
//!     #[snec(entry, receiver({snec::EmptyReceiver}: snec::EmptyReceiver))]
//!     why: String,
//!     #[snec]
//!     random_integer_that_i_like: u128,
//! }
//!
//! struct MyReceiver;
//! impl Receiver<entries::RandomIntegerThatILike> for MyReceiver {
//!     fn receive(&mut self, new_value: &u128) {
//!         println!("My integer has been changed to {}!!", new_value)
//!     }
//! }
//! impl Receiver<entries::WhichYear> for MyReceiver {
//!     fn receive(&mut self, new_value: &i64) {
//!         println!("Resceduled to {}", new_value)
//!     }
//! }
//!
//! let mut config_table = MyConfigTable {
//!     which_year: 1987,
//!     why: "Accident".to_string(),
//!     random_integer_that_i_like: 687_800,
//! };
//! // Now we have receivers which will immediately react to any changes in the values:
//! let mut handle = config_table.get_handle_to::<entries::WhichYear>();
//! {
//!     let mut which_year = handle.modify();
//!     *which_year = 1983;
//! }
//! // When the scope ends, the `which_year` guard is dropped and the receiver is informed.
//! ```

#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;

mod entry;
mod handle;
mod receiver;
pub use entry::*;
pub use handle::*;
pub use receiver::*;

#[cfg(feature = "macros")]
pub extern crate snec_macros as macros;
#[doc(inline)]
pub use macros::*;

// To make derive macros work when called from inside of Snec itself.
extern crate self as snec;