Skip to main content

rskit_config/
lib.rs

1//! Adapter-oriented configuration loading with validation.
2//!
3//! # Example
4//!
5//! ```no_run
6//! use rskit_config::{AppConfig, ConfigLoader, SecretString, ServiceConfig};
7//! use rskit_validation::Validate;
8//! use serde::Deserialize;
9//!
10//! #[derive(Debug, Deserialize)]
11//! struct MyConfig {
12//!     #[serde(flatten)]
13//!     service: ServiceConfig,
14//!     grpc_port: u16,
15//!     api_token: SecretString,
16//! }
17//!
18//! impl Validate for MyConfig {
19//!     fn validate(&self) -> Result<(), validator::ValidationErrors> {
20//!         self.service.validate()?;
21//!         if self.grpc_port == 0 {
22//!             let mut errors = validator::ValidationErrors::new();
23//!             errors.add("grpc_port", validator::ValidationError::new("range"));
24//!             return Err(errors);
25//!         }
26//!         Ok(())
27//!     }
28//! }
29//!
30//! impl AppConfig for MyConfig {
31//!     fn apply_defaults(&mut self) {
32//!         if self.grpc_port == 0 {
33//!             self.grpc_port = 50051;
34//!         }
35//!     }
36//!
37//!     fn service_config(&self) -> &ServiceConfig {
38//!         &self.service
39//!     }
40//! }
41//!
42//! # fn main() -> rskit_errors::AppResult<()> {
43//! let cfg: MyConfig = ConfigLoader::app()
44//!     .with_default("grpc_port", 50051_i64)
45//!     .with_env_prefix("MYAPP")
46//!     .load_app()?;
47//! assert_eq!(cfg.api_token.to_string(), "***");
48//! # Ok(())
49//! # }
50//! ```
51
52#![warn(missing_docs)]
53
54mod loader;
55mod normalize;
56mod service;
57
58pub use loader::{
59    ConfigLoader, ConfigMapSource, ConfigSource, DotenvFileSource, EnvironmentSource, Profile,
60    TomlFileSource, load_config,
61};
62pub use normalize::{canonicalize_root_relative_to, supported_schema};
63pub use rskit_util::SecretString;
64pub use service::{Environment, LogFormat, LogOutput, LoggingConfig, ServiceConfig};
65
66/// Trait that every application config struct must implement.
67///
68/// Typically implemented by a struct that embeds [`ServiceConfig`] and adds
69/// service-specific fields.
70///
71/// ```no_run
72/// use rskit_config::{AppConfig, ConfigLoader, SecretString, ServiceConfig};
73/// use rskit_validation::Validate;
74///
75/// #[derive(serde::Deserialize)]
76/// struct MyConfig {
77///     #[serde(flatten)]
78///     service: ServiceConfig,
79///     grpc_port: u16,
80///     api_token: SecretString,
81/// }
82///
83/// impl Validate for MyConfig {
84///     fn validate(&self) -> Result<(), validator::ValidationErrors> {
85///         self.service.validate()?;
86///         if self.grpc_port == 0 {
87///             let mut errors = validator::ValidationErrors::new();
88///             errors.add("grpc_port", validator::ValidationError::new("range"));
89///             return Err(errors);
90///         }
91///         Ok(())
92///     }
93/// }
94///
95/// impl AppConfig for MyConfig {
96///     fn apply_defaults(&mut self) {
97///         if self.grpc_port == 0 { self.grpc_port = 50051; }
98///     }
99///     fn service_config(&self) -> &ServiceConfig { &self.service }
100/// }
101///
102/// # fn main() -> rskit_errors::AppResult<()> {
103/// let cfg: MyConfig = ConfigLoader::app().load_app()?;
104/// assert_eq!(cfg.api_token.to_string(), "***");
105/// # Ok(())
106/// # }
107/// ```
108pub trait AppConfig:
109    serde::de::DeserializeOwned + rskit_validation::Validate + Send + Sync + 'static
110{
111    /// Apply any programmatic defaults after deserialization.
112    fn apply_defaults(&mut self);
113    /// Return a reference to the embedded [`ServiceConfig`].
114    fn service_config(&self) -> &ServiceConfig;
115}