Skip to main content

hiver_config/
environment.rs

1//! Environment and Profile management
2//! 环境和配置文件管理
3//!
4//! # Equivalent to Spring Boot / 等价于 Spring Boot
5//!
6
7//! - `Environment` - Spring Environment
8//! - `Profile` - Spring @Profile
9//! - `ActiveProfiles` - Active profiles management
10
11use crate::{ConfigError, ConfigResult, PropertySource, Value};
12use indexmap::IndexMap;
13use std::fmt;
14use std::sync::{Arc, RwLock};
15
16/// Environment profile
17/// 环境配置文件
18///
19/// Equivalent to Spring's `@Profile`.
20/// 等价于Spring的`@Profile`。
21///
22/// Common profiles / 常用配置文件:
23/// - `dev` - Development environment
24/// - `test` - Test environment
25/// - `staging` - Staging environment
26/// - `prod` - Production environment
27#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
28pub struct Profile(String);
29
30impl Profile {
31    /// Create a new profile
32    /// 创建新的配置文件
33    pub fn new(name: impl Into<String>) -> Self {
34        Profile(name.into())
35    }
36
37    /// Development profile
38    /// 开发环境
39    pub fn dev() -> Self {
40        Profile("dev".to_string())
41    }
42
43    /// Test profile
44    /// 测试环境
45    pub fn test() -> Self {
46        Profile("test".to_string())
47    }
48
49    /// Staging profile
50    /// 预发布环境
51    pub fn staging() -> Self {
52        Profile("staging".to_string())
53    }
54
55    /// Production profile
56    /// 生产环境
57    pub fn prod() -> Self {
58        Profile("prod".to_string())
59    }
60
61    /// Get profile name
62    /// 获取配置文件名称
63    pub fn name(&self) -> &str {
64        &self.0
65    }
66
67    /// Check if is default profile
68    /// 检查是否为默认配置文件
69    pub fn is_default(&self) -> bool {
70        self.0 == "default"
71    }
72}
73
74impl fmt::Display for Profile {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        write!(f, "{}", self.0)
77    }
78}
79
80impl From<String> for Profile {
81    fn from(s: String) -> Self {
82        Profile(s)
83    }
84}
85
86impl From<&str> for Profile {
87    fn from(s: &str) -> Self {
88        Profile(s.to_string())
89    }
90}
91
92/// Active profiles manager
93/// 活动配置文件管理器
94///
95/// Equivalent to Spring's `ConfigurableEnvironment.setActiveProfiles()`.
96/// 等价于Spring的`ConfigurableEnvironment.setActiveProfiles()`。
97#[derive(Debug, Clone)]
98pub struct ActiveProfiles {
99    profiles: Vec<Profile>,
100    default_profiles: Vec<Profile>,
101}
102
103impl ActiveProfiles {
104    /// Create a new active profiles manager
105    /// 创建新的活动配置文件管理器
106    pub fn new() -> Self {
107        Self {
108            profiles: vec![Profile::dev()],
109            default_profiles: vec![Profile("default".to_string())],
110        }
111    }
112
113    /// Set active profiles
114    /// 设置活动配置文件
115    pub fn set_active(&mut self, profiles: Vec<Profile>) {
116        self.profiles = profiles;
117    }
118
119    /// Add an active profile
120    /// 添加活动配置文件
121    pub fn add_active(&mut self, profile: Profile) {
122        if !self.profiles.contains(&profile) {
123            self.profiles.push(profile);
124        }
125    }
126
127    /// Get active profiles
128    /// 获取活动配置文件
129    pub fn active(&self) -> &[Profile] {
130        &self.profiles
131    }
132
133    /// Check if a profile is active
134    /// 检查配置文件是否活动
135    pub fn is_active(&self, profile: &Profile) -> bool {
136        self.profiles.contains(profile) || self.default_profiles.contains(profile)
137    }
138
139    /// Set default profiles
140    /// 设置默认配置文件
141    pub fn set_defaults(&mut self, profiles: Vec<Profile>) {
142        self.default_profiles = profiles;
143    }
144
145    /// Get default profiles
146    /// 获取默认配置文件
147    pub fn defaults(&self) -> &[Profile] {
148        &self.default_profiles
149    }
150}
151
152impl Default for ActiveProfiles {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158/// Environment interface
159/// 环境接口
160///
161/// Equivalent to Spring's `Environment` interface.
162/// 等价于Spring的`Environment`接口。
163///
164/// Provides access to configuration properties and profiles.
165/// 提供对配置属性和配置文件的访问。
166#[derive(Debug, Clone)]
167pub struct Environment {
168    /// Property sources
169    /// 属性源
170    property_sources: Arc<RwLock<Vec<PropertySource>>>,
171
172    /// Active profiles
173    /// 活动配置文件
174    active_profiles: Arc<RwLock<ActiveProfiles>>,
175
176    /// System environment
177    /// 系统环境
178    system_env: IndexMap<String, String>,
179}
180
181impl Environment {
182    /// Create a new environment
183    /// 创建新的环境
184    pub fn new() -> Self {
185        Self {
186            property_sources: Arc::new(RwLock::new(Vec::new())),
187            active_profiles: Arc::new(RwLock::new(ActiveProfiles::new())),
188            system_env: std::env::vars().collect(),
189        }
190    }
191
192    /// Add a property source
193    /// 添加属性源
194    pub fn add_property_source(&self, source: PropertySource) {
195        let mut sources = self
196            .property_sources
197            .write()
198            .unwrap_or_else(std::sync::PoisonError::into_inner);
199        sources.push(source);
200    }
201
202    /// Add a property source as first (highest priority)
203    /// 添加属性源到第一个(最高优先级)
204    pub fn add_property_source_first(&self, source: PropertySource) {
205        let mut sources = self
206            .property_sources
207            .write()
208            .unwrap_or_else(std::sync::PoisonError::into_inner);
209        sources.insert(0, source);
210    }
211
212    /// Get a property value
213    /// 获取属性值
214    pub fn get_property(&self, key: &str) -> Option<Value> {
215        let sources = self
216            .property_sources
217            .read()
218            .unwrap_or_else(std::sync::PoisonError::into_inner);
219        for source in sources.iter() {
220            if let Some(value) = source.get(key) {
221                return Some(value);
222            }
223        }
224        None
225    }
226
227    /// Get a property as a specific type
228    /// 获取特定类型的属性
229    pub fn get_property_as<T>(&self, key: &str) -> ConfigResult<T>
230    where
231        T: serde::de::DeserializeOwned,
232    {
233        let value = self
234            .get_property(key)
235            .ok_or_else(|| ConfigError::MissingProperty(key.to_string()))?;
236
237        value.into::<T>()
238    }
239
240    /// Get a required property
241    /// 获取必需属性
242    pub fn get_required_property(&self, key: &str) -> ConfigResult<Value> {
243        self.get_property(key)
244            .ok_or_else(|| ConfigError::MissingProperty(key.to_string()))
245    }
246
247    /// Get a required property as a specific type
248    /// 获取特定类型的必需属性
249    pub fn get_required_property_as<T>(&self, key: &str) -> ConfigResult<T>
250    where
251        T: serde::de::DeserializeOwned,
252    {
253        let value = self.get_required_property(key)?;
254        value.into::<T>()
255    }
256
257    /// Check if a property exists
258    /// 检查属性是否存在
259    pub fn contains_property(&self, key: &str) -> bool {
260        self.get_property(key).is_some()
261    }
262
263    /// Resolve placeholders in a string (e.g., ${some.property})
264    /// 解析字符串中的占位符(例如 ${some.property})
265    pub fn resolve_placeholders(&self, input: &str) -> String {
266        let mut result = input.to_string();
267
268        // Find and replace ${...} placeholders
269        let mut start = 0;
270        while let Some(pos) = result[start..].find("${") {
271            let absolute_pos = start + pos;
272            if let Some(end) = result[absolute_pos..].find('}') {
273                let key = &result[absolute_pos + 2..absolute_pos + end];
274                if let Some(value) = self.get_property(key) {
275                    let value_str = value.as_str().unwrap_or_default();
276                    result.replace_range(absolute_pos..=(absolute_pos + end), value_str);
277                }
278                start = absolute_pos + 1;
279            } else {
280                break;
281            }
282        }
283
284        result
285    }
286
287    /// Get active profiles
288    /// 获取活动配置文件
289    pub fn get_active_profiles(&self) -> Vec<Profile> {
290        let profiles = self
291            .active_profiles
292            .read()
293            .unwrap_or_else(std::sync::PoisonError::into_inner);
294        profiles.active().to_vec()
295    }
296
297    /// Set active profiles
298    /// 设置活动配置文件
299    pub fn set_active_profiles(&self, profiles: Vec<Profile>) {
300        let mut active = self
301            .active_profiles
302            .write()
303            .unwrap_or_else(std::sync::PoisonError::into_inner);
304        active.set_active(profiles);
305    }
306
307    /// Add an active profile
308    /// 添加活动配置文件
309    pub fn add_active_profile(&self, profile: Profile) {
310        let mut active = self
311            .active_profiles
312            .write()
313            .unwrap_or_else(std::sync::PoisonError::into_inner);
314        active.add_active(profile);
315    }
316
317    /// Check if a profile is active
318    /// 检查配置文件是否活动
319    pub fn accepts_profiles(&self, profiles: &[Profile]) -> bool {
320        let active = self
321            .active_profiles
322            .read()
323            .unwrap_or_else(std::sync::PoisonError::into_inner);
324        profiles.iter().any(|p| active.is_active(p))
325    }
326
327    /// Get all property sources
328    /// 获取所有属性源
329    pub fn get_property_sources(&self) -> Vec<PropertySource> {
330        let sources = self
331            .property_sources
332            .read()
333            .unwrap_or_else(std::sync::PoisonError::into_inner);
334        sources.clone()
335    }
336
337    /// Get system environment variable
338    /// 获取系统环境变量
339    pub fn get_env(&self, key: &str) -> Option<String> {
340        self.system_env.get(key).cloned()
341    }
342}
343
344impl Default for Environment {
345    fn default() -> Self {
346        Self::new()
347    }
348}
349
350#[cfg(test)]
351mod tests {
352    use super::*;
353
354    // ============================================================
355    // Profile tests / Profile测试
356    // ============================================================
357
358    /// Test Profile creation and name accessor
359    /// 测试Profile创建和名称访问器
360    #[test]
361    fn test_profile_new() {
362        let p = Profile::new("custom");
363        assert_eq!(p.name(), "custom");
364        assert!(!p.is_default());
365    }
366
367    /// Test Profile preset constructors
368    /// 测试Profile预设构造函数
369    #[test]
370    fn test_profile_presets() {
371        assert_eq!(Profile::dev().name(), "dev");
372        assert_eq!(Profile::test().name(), "test");
373        assert_eq!(Profile::staging().name(), "staging");
374        assert_eq!(Profile::prod().name(), "prod");
375    }
376
377    /// Test Profile::is_default
378    /// 测试Profile::is_default
379    #[test]
380    fn test_profile_is_default() {
381        assert!(Profile::new("default").is_default());
382        assert!(!Profile::dev().is_default());
383    }
384
385    /// Test Profile Display trait
386    /// 测试Profile的Display trait
387    #[test]
388    fn test_profile_display() {
389        assert_eq!(format!("{}", Profile::dev()), "dev");
390        assert_eq!(format!("{}", Profile::new("staging")), "staging");
391    }
392
393    /// Test Profile From<String> and From<&str>
394    /// 测试Profile的From<String>和From<&str>
395    #[test]
396    fn test_profile_from() {
397        let p1: Profile = "test".into();
398        let p2: Profile = String::from("prod").into();
399        assert_eq!(p1.name(), "test");
400        assert_eq!(p2.name(), "prod");
401    }
402
403    /// Test Profile equality and ordering
404    /// 测试Profile的相等性和排序
405    #[test]
406    fn test_profile_eq_and_ord() {
407        assert_eq!(Profile::dev(), Profile::new("dev"));
408        assert_ne!(Profile::dev(), Profile::prod());
409        assert!(Profile::dev() < Profile::prod());
410    }
411
412    // ============================================================
413    // ActiveProfiles tests / ActiveProfiles测试
414    // ============================================================
415
416    /// Test ActiveProfiles default starts with dev
417    /// 测试ActiveProfiles默认以dev开始
418    #[test]
419    fn test_active_profiles_default() {
420        let ap = ActiveProfiles::new();
421        assert_eq!(ap.active().len(), 1);
422        assert_eq!(ap.active()[0], Profile::dev());
423    }
424
425    /// Test set_active replaces profiles
426    /// 测试set_active替换配置文件
427    #[test]
428    fn test_active_profiles_set_active() {
429        let mut ap = ActiveProfiles::new();
430        ap.set_active(vec![Profile::prod()]);
431        assert_eq!(ap.active().len(), 1);
432        assert_eq!(ap.active()[0], Profile::prod());
433    }
434
435    /// Test add_active does not duplicate
436    /// 测试add_active不会重复添加
437    #[test]
438    fn test_active_profiles_add_no_duplicate() {
439        let mut ap = ActiveProfiles::new();
440        ap.add_active(Profile::dev());
441        assert_eq!(ap.active().len(), 1); // Still just dev
442    }
443
444    /// Test add_active adds new profile
445    /// 测试add_active添加新配置文件
446    #[test]
447    fn test_active_profiles_add_new() {
448        let mut ap = ActiveProfiles::new();
449        ap.add_active(Profile::prod());
450        assert_eq!(ap.active().len(), 2);
451    }
452
453    /// Test is_active checks both active and default profiles
454    /// 测试is_active同时检查活动配置文件和默认配置文件
455    #[test]
456    fn test_active_profiles_is_active() {
457        let ap = ActiveProfiles::new();
458        assert!(ap.is_active(&Profile::dev()));
459        assert!(ap.is_active(&Profile::new("default"))); // default profile
460        assert!(!ap.is_active(&Profile::prod()));
461    }
462
463    /// Test set_defaults and defaults
464    /// 测试set_defaults和defaults
465    #[test]
466    fn test_active_profiles_defaults() {
467        let mut ap = ActiveProfiles::new();
468        assert_eq!(ap.defaults().len(), 1);
469        assert_eq!(ap.defaults()[0], Profile::new("default"));
470
471        ap.set_defaults(vec![Profile::new("base")]);
472        assert_eq!(ap.defaults().len(), 1);
473        assert_eq!(ap.defaults()[0], Profile::new("base"));
474    }
475
476    // ============================================================
477    // Environment tests / Environment测试
478    // ============================================================
479
480    /// Test Environment creation
481    /// 测试Environment创建
482    #[test]
483    fn test_environment_new() {
484        let env = Environment::new();
485        assert!(env.get_active_profiles().len() >= 1); // default dev
486        assert!(env.get_property_sources().is_empty());
487    }
488
489    /// Test add_property_source and get_property
490    /// 测试add_property_source和get_property
491    #[test]
492    fn test_environment_add_and_get() {
493        let env = Environment::new();
494        let mut source = PropertySource::new("test");
495        source.put("server.port", Value::integer(8080));
496        source.put("server.host", Value::string("localhost"));
497        env.add_property_source(source);
498
499        assert_eq!(env.get_property("server.port").unwrap().as_i64(), Some(8080));
500        assert_eq!(env.get_property("server.host").unwrap().as_str(), Some("localhost"));
501        assert!(env.get_property("nonexistent").is_none());
502    }
503
504    /// Test add_property_source_first gives highest priority
505    /// 测试add_property_source_first给予最高优先级
506    #[test]
507    fn test_environment_add_first_priority() {
508        let env = Environment::new();
509
510        let mut source1 = PropertySource::new("source1");
511        source1.put("key", Value::string("from_source1"));
512        env.add_property_source(source1);
513
514        let mut source2 = PropertySource::new("source2");
515        source2.put("key", Value::string("from_source2"));
516        env.add_property_source_first(source2);
517
518        // source2 was added first, so it should be found first
519        assert_eq!(env.get_property("key").unwrap().as_str(), Some("from_source2"));
520    }
521
522    /// Test get_property_as with type conversion
523    /// 测试带类型转换的get_property_as
524    #[test]
525    fn test_environment_get_property_as() {
526        let env = Environment::new();
527        let mut source = PropertySource::new("test");
528        source.put("count", Value::integer(42));
529        env.add_property_source(source);
530
531        let result: i64 = env.get_property_as("count").unwrap();
532        assert_eq!(result, 42);
533    }
534
535    /// Test get_property_as error on missing key
536    /// 测试键缺失时get_property_as返回错误
537    #[test]
538    fn test_environment_get_property_as_missing() {
539        let env = Environment::new();
540        let result: Result<i64, _> = env.get_property_as("missing");
541        assert!(result.is_err());
542    }
543
544    /// Test get_required_property success and failure
545    /// 测试get_required_property成功和失败
546    #[test]
547    fn test_environment_required_property() {
548        let env = Environment::new();
549        let mut source = PropertySource::new("test");
550        source.put("present", Value::string("here"));
551        env.add_property_source(source);
552
553        assert!(env.get_required_property("present").is_ok());
554        assert!(env.get_required_property("absent").is_err());
555    }
556
557    /// Test contains_property
558    /// 测试contains_property
559    #[test]
560    fn test_environment_contains_property() {
561        let env = Environment::new();
562        assert!(!env.contains_property("key"));
563
564        let mut source = PropertySource::new("test");
565        source.put("key", Value::string("value"));
566        env.add_property_source(source);
567        assert!(env.contains_property("key"));
568    }
569
570    /// Test resolve_placeholders replaces ${key} with property value
571    /// 测试resolve_placeholders将${key}替换为属性值
572    #[test]
573    fn test_environment_resolve_placeholders() {
574        let env = Environment::new();
575        let mut source = PropertySource::new("test");
576        source.put("host", Value::string("localhost"));
577        source.put("port", Value::string("8080"));
578        env.add_property_source(source);
579
580        let result = env.resolve_placeholders("server at ${host}:${port}");
581        assert_eq!(result, "server at localhost:8080");
582    }
583
584    /// Test resolve_placeholders leaves unresolved placeholders as-is
585    /// 测试resolve_placeholders保留未解析的占位符不变
586    #[test]
587    fn test_environment_resolve_placeholders_unresolved() {
588        let env = Environment::new();
589        let result = env.resolve_placeholders("missing ${no.key} stays");
590        assert_eq!(result, "missing ${no.key} stays");
591    }
592
593    /// Test set_active_profiles and get_active_profiles
594    /// 测试set_active_profiles和get_active_profiles
595    #[test]
596    fn test_environment_profiles() {
597        let env = Environment::new();
598        env.set_active_profiles(vec![Profile::prod(), Profile::staging()]);
599
600        let profiles = env.get_active_profiles();
601        assert_eq!(profiles.len(), 2);
602        assert!(profiles.contains(&Profile::prod()));
603        assert!(profiles.contains(&Profile::staging()));
604    }
605
606    /// Test add_active_profile
607    /// 测试add_active_profile
608    #[test]
609    fn test_environment_add_profile() {
610        let env = Environment::new();
611        env.add_active_profile(Profile::test());
612
613        let profiles = env.get_active_profiles();
614        assert!(profiles.contains(&Profile::test()));
615    }
616
617    /// Test accepts_profiles
618    /// 测试accepts_profiles
619    #[test]
620    fn test_environment_accepts_profiles() {
621        let env = Environment::new();
622        assert!(env.accepts_profiles(&[Profile::dev()]));
623        assert!(!env.accepts_profiles(&[Profile::prod()]));
624    }
625
626    /// Test get_property_sources returns all sources
627    /// 测试get_property_sources返回所有源
628    #[test]
629    fn test_environment_get_property_sources() {
630        let env = Environment::new();
631        let source1 = PropertySource::new("s1");
632        let source2 = PropertySource::new("s2");
633        env.add_property_source(source1);
634        env.add_property_source(source2);
635
636        let sources = env.get_property_sources();
637        assert_eq!(sources.len(), 2);
638    }
639
640    /// Test get_env retrieves system environment variable
641    /// 测试get_env获取系统环境变量
642    #[test]
643    fn test_environment_get_env() {
644        let env = Environment::new();
645        // PATH should exist on any system
646        assert!(env.get_env("PATH").is_some());
647        // A made-up variable should not exist
648        assert!(env.get_env("HIVER_TEST_NONEXISTENT_VAR_12345").is_none());
649    }
650
651    /// Test get_required_property_as with typed value
652    /// 测试带类型值的get_required_property_as
653    #[test]
654    fn test_environment_get_required_property_as() {
655        let env = Environment::new();
656        let mut source = PropertySource::new("test");
657        source.put("ratio", Value::float(2.5));
658        env.add_property_source(source);
659
660        let result: f64 = env.get_required_property_as("ratio").unwrap();
661        assert!((result - 2.5).abs() < f64::EPSILON);
662    }
663}