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 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
//! Salak is a multi layered configuration loader and zero-boilerplate configuration parser, with many predefined sources.
//!
//! 1. [About](#about)
//! 2. [Quick Start](#quick-start)
//! 3. [Features](#features)
//! * [Predefined Sources](#predefined-sources)
//! * [Key Convention](#key-convention)
//! * [Value Placeholder Parsing](#value-placeholder-parsing)
//! * [Attributes For Derive](#attributes-for-derive)
//! * [Reload Configuration](#reload-configuration)
//! * [Resource Factory](#resource-factory)
//!
//! ## About
//! `salak` is a multi layered configuration loader with many predefined sources. Also it
//! is a zero-boilerplate configuration parser which provides an auto-derive procedure macro
//! to derive [`FromEnvironment`] so that we can parse configuration structs without any additional codes.
//!
//! ## Quick Start
//! A simple example of `salak`:
//!
//! ```
//! use salak::*;
//!
//! #[derive(Debug, FromEnvironment)]
//! #[salak(prefix = "config")]
//! struct Config {
//! #[salak(default = false)]
//! verbose: bool,
//! optional: Option<String>,
//! #[salak(name = "val")]
//! value: i64,
//! }
//! let env = Salak::builder()
//! .set("config.val", "2021")
//! .build()
//! .unwrap();
//! let config = env.get::<Config>().unwrap();
//! assert_eq!(2021, config.value);
//! assert_eq!(None, config.optional);
//! assert_eq!(false, config.verbose);
//! ```
//!
//! ## Features
//!
//! #### Predefined Sources
//! Predefined sources has the following order, [`Salak`] will find by sequence of these orders,
//! if the property with specified key is found at the current source, than return immediately. Otherwise,
//! it will search the next source.
//!
//! 1. Random source provides a group of keys can return random values.
//! * `random.u8`
//! * `random.u16`
//! * `random.u32`
//! * `random.u64`
//! * `random.u128`
//! * `random.usize`
//! * `random.i8`
//! * `random.i16`
//! * `random.i32`
//! * `random.i64`
//! * `random.i128`
//! * `random.isize`
//! 2. Custom arguments source. [`SalakBuilder::set()`] can set a single kv,
//! and [`SalakBuilder::set_args()`] can set a group of kvs.
//! 3. System environment source. Implemented by [`source::system_environment`].
//! 4. Profile specified file source, eg. `app-dev.toml`, supports reloading.
//! 5. No profile file source, eg. `app.toml`, supports reloading.
//! 6. Custom sources, which can register by [`Salak::register()`].
//!
//! #### Key Convention
//! Key is used for search configuration from [`Environment`], normally it is represented by string.
//! Key is a group of SubKey separated by dot(`.`), and SubKey is a name or a name followed by index.
//! 1. SubKey Format (`[a-z][_a-z0-9]+(\[[0-9]+\])*`)
//! * `a`
//! * `a0`
//! * `a_b`
//! * `a[0]`
//! * `a[0][0]`
//! 2. Key Format (`SubKey(\.SubKey)*`)
//! * `a`
//! * `a.b`
//! * `a.val[0]`
//! * `a_b[0]`
//!
//! #### Value Placeholder Parsing
//! 1. Placeholder Format
//! * `${key}` => Get value of `key`.
//! * `${key:default}` => Get value of `key`, if not exists return `default`.
//! 2. Escape Format
//! * `\$\{key\}` => Return `${key}`.
//! * `$`, `\`, `{`, `}` must use escape format.
//!
//! #### Attributes For Derive
//! `salak` supports some attributes for automatically derive [`FromEnvironment`].
//! All attributes have format `#[salak(..)]`, eg. `#[salak(default = "default value")]`.
//! 1. Struct Header Attribute.
//! * `#[salak(prefix = "salak.application")]`, has this attr will auto implement [`PrefixedFromEnvironment`].
//! 2. Struct Field Attribute.
//! * `#[salak(default = "value")]`, this attr can specify default value.
//! * `#[salak(name = "key")]`, this attr can specify property key, default convension is use field name.
//! * `#[salak(desc = "Field Description")]`, this attr can be describe this property.
//!
//! #### Reload Configuration
//! `salak` supports reload configurations. Since in rust mutable
//! and alias can't be used together, here we introduce a wrapper
//! [`wrapper::IORef`] for updating values when reloading.
//!
//! #### Resource Factory
//! [`Resource`] defines a standard way to create instance. [`Factory`] provides functions to initialize resource
//! and cache resource. Please refer to [salak_factory](https://docs.rs/salak_factory) for resource usage.
//! Feature 'app' should be open for this feature.
//!
#![cfg_attr(docsrs, feature(doc_cfg))]
#![warn(
anonymous_parameters,
missing_copy_implementations,
missing_debug_implementations,
missing_docs,
nonstandard_style,
rust_2018_idioms,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
unreachable_pub,
unused_extern_crates,
unused_qualifications,
variant_size_differences
)]
use parking_lot::Mutex;
#[cfg(feature = "derive")]
use crate::derive::KeyDesc;
#[cfg(feature = "derive")]
mod derive;
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use crate::derive::{
AutoDeriveFromEnvironment, DescFromEnvironment, PrefixedFromEnvironment, SalakDescContext,
};
use raw_ioref::IORefT;
/// Auto derive [`FromEnvironment`] for struct.
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
pub use salak_derive::FromEnvironment;
/// Auto derive [`Service`] for struct.
#[cfg(all(feature = "derive", feature = "app"))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "derive", feature = "app"))))]
pub use salak_derive::Service;
use source_raw::PropertyRegistryInternal;
#[cfg(feature = "args")]
#[cfg_attr(docsrs, doc(cfg(feature = "args")))]
mod args;
#[cfg(feature = "args")]
#[cfg_attr(docsrs, doc(cfg(feature = "args")))]
pub use crate::args::AppInfo;
mod err;
mod raw;
use crate::raw::SubKey;
pub use crate::raw::{IsProperty, Property};
mod raw_ioref;
mod raw_vec;
use crate::env::PREFIX;
pub use crate::env::{Salak, SalakBuilder};
mod env;
mod raw_enum;
pub use crate::err::PropertyError;
pub use crate::raw_enum::EnumProperty;
mod source_map;
#[cfg(feature = "rand")]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
mod source_rand;
mod source_raw;
#[cfg(feature = "toml")]
#[cfg_attr(docsrs, doc(cfg(feature = "toml")))]
mod source_toml;
#[cfg(feature = "yaml")]
#[cfg_attr(docsrs, doc(cfg(feature = "yaml")))]
mod source_yaml;
use crate::source::Key;
use crate::source::SubKeys;
#[cfg(feature = "app")]
#[cfg_attr(docsrs, doc(cfg(feature = "app")))]
mod app;
#[cfg(feature = "app")]
#[cfg_attr(docsrs, doc(cfg(feature = "app")))]
pub use crate::app::*;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;
/// Salak wrapper for configuration parsing.
///
/// Wrapper can determine extra behavior for parsing.
/// Such as check empty of vec or update when reloading.
pub mod wrapper {
pub use crate::raw_ioref::IORef;
pub use crate::raw_vec::NonEmptyVec;
}
/// Salak sources.
///
/// This mod exports all pub sources.
pub mod source {
#[cfg(feature = "args")]
#[cfg_attr(docsrs, doc(cfg(feature = "args")))]
pub(crate) use crate::args::from_args;
pub use crate::raw::Key;
pub use crate::raw::SubKeys;
pub use crate::source_map::system_environment;
pub use crate::source_map::HashMapSource;
}
pub(crate) type Res<T> = Result<T, PropertyError>;
pub(crate) type Void = Res<()>;
/// A property source defines how to load properties.
/// `salak` has some predefined sources, user can
/// provide custom source by implementing this trait.
///
/// Sources provided by `salak`.
///
/// * hashmap source
/// * std::env source
/// * toml source
/// * yaml source
pub trait PropertySource: Send + Sync {
/// [`PropertySource`] name.
fn name(&self) -> &str;
/// Get property by key.
fn get_property(&self, key: &Key<'_>) -> Option<Property<'_>>;
/// Get all subkeys with given key.
///
/// Subkeys are keys without dot('.').
/// This method is unstable, and will be simplified by hidding
/// Key and SubKeys.
fn get_sub_keys<'a>(&'a self, key: &Key<'_>, sub_keys: &mut SubKeys<'a>);
/// Check whether the [`PropertySource`] is empty.
/// Empty source will be ignored when registering to `salak`.
fn is_empty(&self) -> bool;
/// Reload source, if nothing changes, then return none.
#[inline]
fn reload_source(&self) -> Res<Option<Box<dyn PropertySource>>> {
Ok(None)
}
}
/// Environment defines interface for getting values, and reloading
/// configurations.
///
/// The implementor of this trait is [`Salak`].
pub trait Environment {
/// Get value by key.
/// * `key` - Configuration key.
///
/// Require means is if the value `T` is not found,
/// then error will be returned. But if you try to get
/// `Option<T>`, then not found will return `None`.
fn require<T: FromEnvironment>(&self, key: &str) -> Res<T>;
/// Reload configuration. If reloading is completed,
/// all values wrapped by [`wrapper::IORef`] will be updated.
///
/// Currently, this feature is unstable, the returned bool
/// value means reloading is completed without error.
fn reload(&self) -> Res<bool>;
#[cfg(feature = "derive")]
#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
#[inline]
/// Get value with predefined key.
///
/// [`PrefixedFromEnvironment`] can be auto derives by
/// [`salak_derive::FromEnvironment`] macro. It provides
/// a standard key for getting value `T`.
fn get<T: PrefixedFromEnvironment>(&self) -> Res<T> {
self.require::<T>(T::prefix())
}
}
/// Context for implementing [`FromEnvironment`].
#[allow(missing_debug_implementations)]
pub struct SalakContext<'a> {
registry: &'a PropertyRegistryInternal<'a>,
iorefs: &'a Mutex<Vec<Box<dyn IORefT + Send>>>,
key: &'a mut Key<'a>,
}
/// Parsing value from environment by [`SalakContext`].
pub trait FromEnvironment: Sized {
/// Generate object from [`SalakContext`].
/// * `val` - Property value can be parsed from.
/// * `env` - Context.
///
/// ```no_run
/// use salak::*;
/// pub struct Config {
/// key: String
/// }
/// impl FromEnvironment for Config {
/// fn from_env(
/// val: Option<Property<'_>>,
/// env: &mut SalakContext<'_>,
/// ) -> Result<Self, PropertyError> {
/// Ok(Self{
/// key: env.require_def("key", None)?,
/// })
/// }
/// }
///
/// ```
fn from_env(val: Option<Property<'_>>, env: &mut SalakContext<'_>) -> Res<Self>;
}