From 6ae61c93257562c0994a64921ac36aca7aeadb7f Mon Sep 17 00:00:00 2001 From: Robert David Date: Thu, 11 Jul 2024 23:11:31 +0200 Subject: [PATCH] Add a global_module example for a singleton configuration module deserialized to a predefined structures --- examples/global_module/config/default.toml | 5 ++ examples/global_module/cred.rs | 13 ++++ examples/global_module/main.rs | 20 +++++ examples/global_module/settings.rs | 86 ++++++++++++++++++++++ 4 files changed, 124 insertions(+) create mode 100644 examples/global_module/config/default.toml create mode 100644 examples/global_module/cred.rs create mode 100644 examples/global_module/main.rs create mode 100644 examples/global_module/settings.rs diff --git a/examples/global_module/config/default.toml b/examples/global_module/config/default.toml new file mode 100644 index 00000000..bd9ec38b --- /dev/null +++ b/examples/global_module/config/default.toml @@ -0,0 +1,5 @@ +verbose=1 + +[cred] +user="robert" +key="123456789" diff --git a/examples/global_module/cred.rs b/examples/global_module/cred.rs new file mode 100644 index 00000000..0c02d3db --- /dev/null +++ b/examples/global_module/cred.rs @@ -0,0 +1,13 @@ +use super::settings::Settings; + +pub fn list_cred() { + match Settings::user() { + Ok(u) => println!("My name is: {u}"), + Err(e) => println!("{e}") + } + + match Settings::key() { + Ok(k) => println!("My key is: {k}"), + Err(e) => println!("{e}") + } +} diff --git a/examples/global_module/main.rs b/examples/global_module/main.rs new file mode 100644 index 00000000..9e2a5dc9 --- /dev/null +++ b/examples/global_module/main.rs @@ -0,0 +1,20 @@ +mod settings; +mod cred; + +use crate::settings::Settings; +use crate::cred::list_cred; + +fn main() { + // init the config module + Settings::init(Some("examples/global_module/config/default.toml")); + + // now your config may be used anywhere in the code where you are able to + // use "crate::settings" or "super::settings". + let verbosity = Settings::verbosity(); + + if verbosity > 0 { + println!("Hello world, verbosity setting is greater than 0"); + } + + list_cred(); +} diff --git a/examples/global_module/settings.rs b/examples/global_module/settings.rs new file mode 100644 index 00000000..e4b2783a --- /dev/null +++ b/examples/global_module/settings.rs @@ -0,0 +1,86 @@ +use config::{Config, File}; +use serde_derive::Deserialize; +use std::sync::OnceLock; +use std::sync::RwLock; + + +#[derive(Default, Clone, Deserialize)] +struct Cred { + user: String, + key: String, +} + +#[derive(Default, Clone, Deserialize)] +pub struct Settings { + verbose: Option, + cred: Option, +} + + +// This function defines the static settings storage. +fn settings() -> &'static RwLock { + static SETTINGS: OnceLock> = OnceLock::new(); + SETTINGS.get_or_init(|| RwLock::new(Settings::default())) +} + +fn build_config(file: &str) -> Settings { + let s = Config::builder() + // Configuration file + .add_source(File::with_name(file).required(false)) + .build() + .expect("Config build failed"); + + // Deserialize (and thus freeze) the entire configuration + s.try_deserialize().unwrap() +} + +impl Settings { + // This associated function replaces previous settings values with a newly + // loaded ones. + // + // It is mainly intended for loading the values from config file to replace + // the plain default used for static allocation. Thus running it once at + // the beginning of the program execution when the config files are known. + // + // But a later call to this function may be used to update the settings, + // for example, when the config file changes during the execution and you want + // to sync with it (signal/notify/whatever based reload). + pub fn init(cfgfile: Option<&str>) { + let file = cfgfile.unwrap_or("config.toml"); + + let mut new_settings = settings().write().unwrap(); + *new_settings = build_config(file); + } + + // Following associated functions are just getters, when you want to keep + // the Settings structure members private. + pub fn user() -> Result { + match &settings().read().unwrap().cred { + Some(c) => Ok(c.user.clone()), + None => Err("Credential config is missing".to_string()), + } + } + + pub fn key() -> Result { + match &settings().read().unwrap().cred { + Some(c) => Ok(c.key.clone()), + None => Err("Credential config is missing".to_string()), + } + } + + pub fn verbosity() -> u8 { + settings().read().unwrap().verbose.unwrap_or(0) + } + + // It is not a problem to make all the Settings structure members public and + // then only create here one function, that will return a read reference + // to the Settings. This may be useful if you want to omit the getters and + // the settings contain just plain values that don't need any error + // handling. + // + // Example: + // pub fn new() -> Self { + // settings().read().unwrap() + // } +} +