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}