Skip to main content

rskit_config/service/
config.rs

1use rskit_validation::Validate;
2use serde::Deserialize;
3use validator::{ValidationError, ValidationErrors};
4
5use super::environment::Environment;
6use rskit_logging::LoggingConfig;
7
8/// Base service configuration — embed this in every application config.
9#[derive(Debug, Clone, Deserialize)]
10pub struct ServiceConfig {
11    /// Service name used in logs, traces, and health responses.
12    #[serde(default = "ServiceConfig::default_name")]
13    pub name: String,
14
15    /// Deployment environment (development, staging, production).
16    #[serde(default)]
17    pub environment: Environment,
18
19    /// Semver version string, defaults to `CARGO_PKG_VERSION`.
20    #[serde(default = "ServiceConfig::default_version")]
21    pub version: String,
22
23    /// Network address the service binds to.
24    #[serde(default = "ServiceConfig::default_address")]
25    pub address: String,
26
27    /// Network port the service listens on.
28    #[serde(default = "ServiceConfig::default_port")]
29    pub port: u16,
30
31    /// Enable verbose debug output.
32    #[serde(default)]
33    pub debug: bool,
34
35    /// Logging configuration.
36    #[serde(default)]
37    pub logging: LoggingConfig,
38}
39
40impl Validate for ServiceConfig {
41    fn validate(&self) -> Result<(), ValidationErrors> {
42        let mut errors = ValidationErrors::new();
43        if self.name.trim().is_empty() {
44            errors.add("name", ValidationError::new("length"));
45        }
46        if errors.is_empty() {
47            Ok(())
48        } else {
49            Err(errors)
50        }
51    }
52}
53
54impl ServiceConfig {
55    fn default_name() -> String {
56        "service".to_string()
57    }
58    fn default_version() -> String {
59        rskit_version::package_version().to_string()
60    }
61    fn default_address() -> String {
62        "0.0.0.0".to_string()
63    }
64    fn default_port() -> u16 {
65        50051
66    }
67}
68
69impl Default for ServiceConfig {
70    fn default() -> Self {
71        Self {
72            name: Self::default_name(),
73            environment: Environment::default(),
74            version: Self::default_version(),
75            address: Self::default_address(),
76            port: Self::default_port(),
77            debug: false,
78            logging: LoggingConfig::default(),
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::service::{LogFormat, LogOutput};
87
88    #[test]
89    fn service_config_default_name() {
90        let cfg = ServiceConfig::default();
91        assert_eq!(cfg.name, "service");
92    }
93
94    #[test]
95    fn service_config_default_environment() {
96        let cfg = ServiceConfig::default();
97        assert_eq!(cfg.environment, Environment::Development);
98    }
99
100    #[test]
101    fn service_config_default_debug_false() {
102        let cfg = ServiceConfig::default();
103        assert!(!cfg.debug);
104    }
105
106    #[test]
107    fn service_config_default_version_is_cargo_pkg() {
108        let cfg = ServiceConfig::default();
109        assert_eq!(cfg.version, rskit_version::package_version());
110    }
111
112    #[test]
113    fn service_config_default_logging() {
114        let cfg = ServiceConfig::default();
115        assert_eq!(cfg.logging.level, "info");
116        assert_eq!(cfg.logging.format, LogFormat::Console);
117        assert_eq!(cfg.logging.output, LogOutput::Stdout);
118    }
119
120    #[test]
121    fn service_config_validation_empty_name_fails() {
122        let cfg = ServiceConfig {
123            name: String::new(),
124            ..Default::default()
125        };
126        assert!(cfg.validate().is_err());
127    }
128
129    #[test]
130    fn service_config_validation_valid_name_passes() {
131        let cfg = ServiceConfig::default();
132        assert!(cfg.validate().is_ok());
133    }
134
135    #[test]
136    fn service_config_validation_long_name_passes() {
137        let cfg = ServiceConfig {
138            name: "a".repeat(1000),
139            ..Default::default()
140        };
141        assert!(cfg.validate().is_ok());
142    }
143
144    #[test]
145    fn service_config_all_fields_accessible() {
146        let cfg = ServiceConfig::default();
147        let _ = &cfg.name;
148        let _ = &cfg.environment;
149        let _ = &cfg.version;
150        let _ = &cfg.address;
151        let _ = cfg.port;
152        let _ = cfg.debug;
153        let _ = &cfg.logging;
154    }
155
156    #[test]
157    fn service_config_default_address() {
158        let cfg = ServiceConfig::default();
159        assert_eq!(cfg.address, "0.0.0.0");
160    }
161
162    #[test]
163    fn service_config_default_port() {
164        let cfg = ServiceConfig::default();
165        assert_eq!(cfg.port, 50051);
166    }
167
168    #[test]
169    fn service_config_debug_format() {
170        let cfg = ServiceConfig::default();
171        let debug_str = format!("{:?}", cfg);
172        assert!(debug_str.contains("ServiceConfig"));
173        assert!(debug_str.contains("service"));
174    }
175}