Skip to main content

hiver_config/
source.rs

1//! Property source module
2//! 属性源模块
3//!
4//! # Equivalent to Spring Boot / 等价于 Spring Boot
5//!
6//! - `PropertySource` - Spring `PropertySource`
7//! - `PropertySource.Builder` - Spring PropertySource.Builder
8//! - `PropertySource.Order` - Property source ordering/priority
9
10use crate::Value;
11use indexmap::IndexMap;
12use std::collections::HashMap;
13use std::path::PathBuf;
14
15/// Property source type
16/// 属性源类型
17///
18/// Equivalent to Spring's `PropertySource` types.
19/// `等价于Spring的PropertySource类型`。
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum PropertySourceType {
22    /// Command line arguments
23    /// 命令行参数
24    CommandLine,
25
26    /// System environment
27    /// 系统环境
28    SystemEnvironment,
29
30    /// System properties
31    /// 系统属性
32    SystemProperties,
33
34    /// Application properties
35    /// 应用属性
36    ApplicationProperties,
37
38    /// Application YAML
39    /// 应用YAML
40    ApplicationYaml,
41
42    /// Application TOML
43    /// 应用TOML
44    ApplicationToml,
45
46    /// External configuration
47    /// 外部配置
48    External,
49
50    /// Custom source
51    /// 自定义源
52    Custom,
53}
54
55impl PropertySourceType {
56    /// Get the default order for this type (lower = higher priority)
57    /// 获取此类型的默认顺序(越小优先级越高)
58    pub fn default_order(&self) -> u32 {
59        match self {
60            PropertySourceType::CommandLine => 100,
61            PropertySourceType::SystemEnvironment => 200,
62            PropertySourceType::SystemProperties => 300,
63            PropertySourceType::ApplicationProperties => 400,
64            PropertySourceType::ApplicationYaml => 500,
65            PropertySourceType::ApplicationToml => 600,
66            PropertySourceType::External => 700,
67            PropertySourceType::Custom => 800,
68        }
69    }
70}
71
72/// Property source
73/// 属性源
74///
75/// Equivalent to Spring's `PropertySource`.
76/// 等价于Spring的`PropertySource`。
77///
78/// Represents a source of configuration properties with a name and priority.
79/// 表示具有名称和优先级的配置属性源。
80#[derive(Debug, Clone)]
81pub struct PropertySource {
82    /// Name of the property source
83    /// 属性源名称
84    name: String,
85
86    /// Properties map
87    /// 属性映射
88    properties: IndexMap<String, Value>,
89
90    /// Source type
91    /// 源类型
92    source_type: PropertySourceType,
93
94    /// Order (lower = higher priority)
95    /// 顺序(越小优先级越高)
96    order: u32,
97
98    /// File path (if loaded from file)
99    /// 文件路径(如果从文件加载)
100    file_path: Option<PathBuf>,
101}
102
103impl PropertySource {
104    /// Create a new property source
105    /// 创建新的属性源
106    pub fn new(name: impl Into<String>) -> Self {
107        let name = name.into();
108        let source_type = Self::infer_source_type(&name);
109
110        Self {
111            name,
112            properties: IndexMap::new(),
113            source_type,
114            order: source_type.default_order(),
115            file_path: None,
116        }
117    }
118
119    /// Create with map
120    /// 使用映射创建
121    pub fn with_map(name: impl Into<String>, map: HashMap<String, Value>) -> Self {
122        let mut source = Self::new(name);
123        source.properties = map.into_iter().collect();
124        source
125    }
126
127    /// Infer source type from name
128    /// 从名称推断源类型
129    fn infer_source_type(name: &str) -> PropertySourceType {
130        let lower = name.to_lowercase();
131        if lower.contains("command") || lower.contains("argv") {
132            PropertySourceType::CommandLine
133        } else if lower.contains("env") {
134            PropertySourceType::SystemEnvironment
135        } else if lower.contains("yaml") || lower.contains("yml") {
136            PropertySourceType::ApplicationYaml
137        } else if lower.contains("toml") {
138            PropertySourceType::ApplicationToml
139        } else if lower.contains("properties") || lower.contains("props") {
140            PropertySourceType::ApplicationProperties
141        } else if lower.contains("external") {
142            PropertySourceType::External
143        } else {
144            PropertySourceType::Custom
145        }
146    }
147
148    /// Get property source name
149    /// 获取属性源名称
150    pub fn name(&self) -> &str {
151        &self.name
152    }
153
154    /// Get all properties
155    /// 获取所有属性
156    pub fn properties(&self) -> &IndexMap<String, Value> {
157        &self.properties
158    }
159
160    /// Get source type
161    /// 获取源类型
162    pub fn source_type(&self) -> PropertySourceType {
163        self.source_type
164    }
165
166    /// Get order
167    /// 获取顺序
168    pub fn order(&self) -> u32 {
169        self.order
170    }
171
172    /// Get file path
173    /// 获取文件路径
174    pub fn file_path(&self) -> Option<&PathBuf> {
175        self.file_path.as_ref()
176    }
177
178    /// Set file path
179    /// 设置文件路径
180    pub fn set_file_path(&mut self, path: PathBuf) {
181        self.file_path = Some(path);
182    }
183
184    /// Set order
185    /// 设置顺序
186    pub fn set_order(&mut self, order: u32) {
187        self.order = order;
188    }
189
190    /// Add a property
191    /// 添加属性
192    pub fn put(&mut self, key: impl Into<String>, value: impl Into<Value>) {
193        self.properties.insert(key.into(), value.into());
194    }
195
196    /// Get a property value
197    /// 获取属性值
198    pub fn get(&self, key: &str) -> Option<Value> {
199        self.properties.get(key).cloned()
200    }
201
202    /// Check if contains key
203    /// 检查是否包含键
204    pub fn contains_key(&self, key: &str) -> bool {
205        self.properties.contains_key(key)
206    }
207
208    /// Remove a property
209    /// 移除属性
210    pub fn remove(&mut self, key: &str) -> Option<Value> {
211        self.properties.shift_remove(key)
212    }
213
214    /// Get all keys
215    /// 获取所有键
216    pub fn keys(&self) -> impl Iterator<Item = &String> {
217        self.properties.keys()
218    }
219
220    /// Iterate over all properties
221    /// 遍历所有属性
222    pub fn iter(&self) -> impl Iterator<Item = (&String, &Value)> {
223        self.properties.iter()
224    }
225
226    /// Get number of properties
227    /// 获取属性数量
228    pub fn len(&self) -> usize {
229        self.properties.len()
230    }
231
232    /// Check if empty
233    /// 检查是否为空
234    pub fn is_empty(&self) -> bool {
235        self.properties.is_empty()
236    }
237
238    /// Merge another property source into this one
239    /// 合并另一个属性源到此属性源
240    pub fn merge(&mut self, other: PropertySource) {
241        for (key, value) in other.properties {
242            self.properties.insert(key, value);
243        }
244    }
245}
246
247/// Property source builder
248/// 属性源构建器
249///
250/// Equivalent to Spring's `PropertySource.Builder`.
251/// 等价于Spring的`PropertySource.Builder`。
252pub struct PropertySourceBuilder {
253    source: PropertySource,
254}
255
256impl PropertySourceBuilder {
257    /// Create a new builder
258    /// 创建新的构建器
259    pub fn new(name: impl Into<String>) -> Self {
260        Self {
261            source: PropertySource::new(name),
262        }
263    }
264
265    /// Set source type
266    /// 设置源类型
267    pub fn source_type(mut self, source_type: PropertySourceType) -> Self {
268        self.source.source_type = source_type;
269        self
270    }
271
272    /// Set order
273    /// 设置顺序
274    pub fn order(mut self, order: u32) -> Self {
275        self.source.order = order;
276        self
277    }
278
279    /// Set file path
280    /// 设置文件路径
281    pub fn file_path(mut self, path: PathBuf) -> Self {
282        self.source.file_path = Some(path);
283        self
284    }
285
286    /// Add a property
287    /// 添加属性
288    pub fn put(&mut self, key: impl Into<String>, value: impl Into<Value>) -> &mut Self {
289        self.source.put(key, value);
290        self
291    }
292
293    /// Add all properties from a map
294    /// 从映射添加所有属性
295    pub fn put_all(&mut self, map: HashMap<String, Value>) -> &mut Self {
296        for (key, value) in map {
297            self.source.put(key, value);
298        }
299        self
300    }
301
302    /// Build the property source
303    /// 构建属性源
304    pub fn build(self) -> PropertySource {
305        self.source
306    }
307}
308
309impl Default for PropertySource {
310    fn default() -> Self {
311        Self::new("default")
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318
319    // ============================================================
320    // PropertySourceType tests / 属性源类型测试
321    // ============================================================
322
323    /// Test default order for each source type
324    /// 测试每种源类型的默认顺序
325    #[test]
326    fn test_source_type_default_order() {
327        assert_eq!(PropertySourceType::CommandLine.default_order(), 100);
328        assert_eq!(PropertySourceType::SystemEnvironment.default_order(), 200);
329        assert_eq!(PropertySourceType::SystemProperties.default_order(), 300);
330        assert_eq!(PropertySourceType::ApplicationProperties.default_order(), 400);
331        assert_eq!(PropertySourceType::ApplicationYaml.default_order(), 500);
332        assert_eq!(PropertySourceType::ApplicationToml.default_order(), 600);
333        assert_eq!(PropertySourceType::External.default_order(), 700);
334        assert_eq!(PropertySourceType::Custom.default_order(), 800);
335    }
336
337    /// Test that CommandLine has the highest priority (lowest order number)
338    /// 测试CommandLine具有最高优先级(最低顺序号)
339    #[test]
340    fn test_source_type_priority_ordering() {
341        assert!(
342            PropertySourceType::CommandLine.default_order()
343                < PropertySourceType::SystemEnvironment.default_order()
344        );
345        assert!(
346            PropertySourceType::SystemEnvironment.default_order()
347                < PropertySourceType::ApplicationProperties.default_order()
348        );
349        assert!(
350            PropertySourceType::ApplicationProperties.default_order()
351                < PropertySourceType::Custom.default_order()
352        );
353    }
354
355    // ============================================================
356    // PropertySource creation and operations tests / PropertySource创建和操作测试
357    // ============================================================
358
359    /// Test creating a new PropertySource and verify inferred type
360    /// 测试创建新的PropertySource并验证推断的类型
361    #[test]
362    fn test_new_property_source() {
363        let source = PropertySource::new("test-source");
364        assert_eq!(source.name(), "test-source");
365        assert_eq!(source.source_type(), PropertySourceType::Custom);
366        assert!(source.is_empty());
367        assert_eq!(source.len(), 0);
368        assert!(source.file_path().is_none());
369    }
370
371    /// Test infer source type from name: command line
372    /// 测试从名称推断源类型:命令行
373    #[test]
374    fn test_infer_source_type_command_line() {
375        let source = PropertySource::new("commandArgs");
376        assert_eq!(source.source_type(), PropertySourceType::CommandLine);
377    }
378
379    /// Test infer source type from name: environment
380    /// 测试从名称推断源类型:环境变量
381    #[test]
382    fn test_infer_source_type_environment() {
383        let source = PropertySource::new("envVars");
384        assert_eq!(source.source_type(), PropertySourceType::SystemEnvironment);
385    }
386
387    /// Test infer source type from name: YAML
388    /// 测试从名称推断源类型:YAML
389    #[test]
390    fn test_infer_source_type_yaml() {
391        let source = PropertySource::new("application-yaml");
392        assert_eq!(source.source_type(), PropertySourceType::ApplicationYaml);
393    }
394
395    /// Test infer source type from name: TOML
396    /// 测试从名称推断源类型:TOML
397    #[test]
398    fn test_infer_source_type_toml() {
399        let source = PropertySource::new("config.toml");
400        assert_eq!(source.source_type(), PropertySourceType::ApplicationToml);
401    }
402
403    /// Test infer source type from name: properties
404    /// 测试从名称推断源类型:properties
405    #[test]
406    fn test_infer_source_type_properties() {
407        let source = PropertySource::new("app-properties");
408        assert_eq!(source.source_type(), PropertySourceType::ApplicationProperties);
409    }
410
411    /// Test infer source type from name: external
412    /// 测试从名称推断源类型:external
413    #[test]
414    fn test_infer_source_type_external() {
415        let source = PropertySource::new("external-config");
416        assert_eq!(source.source_type(), PropertySourceType::External);
417    }
418
419    /// Test PropertySource::with_map creation
420    /// 测试PropertySource::with_map创建
421    #[test]
422    fn test_with_map() {
423        let mut map = HashMap::new();
424        map.insert("key1".to_string(), Value::string("value1"));
425        map.insert("key2".to_string(), Value::integer(42));
426
427        let source = PropertySource::with_map("test-map", map);
428        assert_eq!(source.len(), 2);
429        assert!(source.contains_key("key1"));
430        assert!(source.contains_key("key2"));
431        assert_eq!(source.get("key1").unwrap().as_str(), Some("value1"));
432        assert_eq!(source.get("key2").unwrap().as_i64(), Some(42));
433    }
434
435    /// Test put, get, contains_key, remove operations
436    /// 测试put、get、contains_key、remove操作
437    #[test]
438    fn test_put_get_remove() {
439        let mut source = PropertySource::new("test");
440        assert!(!source.contains_key("name"));
441
442        source.put("name", "hiver");
443        assert!(source.contains_key("name"));
444        assert_eq!(source.get("name").unwrap().as_str(), Some("hiver"));
445
446        // Overwrite existing key
447        source.put("name", "updated");
448        assert_eq!(source.get("name").unwrap().as_str(), Some("updated"));
449
450        // Remove key
451        let removed = source.remove("name");
452        assert_eq!(removed.unwrap().as_str(), Some("updated"));
453        assert!(!source.contains_key("name"));
454    }
455
456    /// Test keys and iter methods
457    /// 测试keys和iter方法
458    #[test]
459    fn test_keys_and_iter() {
460        let mut source = PropertySource::new("test");
461        source.put("a", 1);
462        source.put("b", 2);
463        source.put("c", 3);
464
465        let keys: Vec<&String> = source.keys().collect();
466        assert_eq!(keys.len(), 3);
467
468        let entries: Vec<_> = source.iter().collect();
469        assert_eq!(entries.len(), 3);
470    }
471
472    /// Test set_order and set_file_path
473    /// 测试set_order和set_file_path
474    #[test]
475    fn test_order_and_file_path() {
476        let mut source = PropertySource::new("test");
477        assert_eq!(source.order(), PropertySourceType::Custom.default_order());
478
479        source.set_order(50);
480        assert_eq!(source.order(), 50);
481
482        source.set_file_path(PathBuf::from("/etc/hiver/app.yaml"));
483        assert_eq!(source.file_path(), Some(&PathBuf::from("/etc/hiver/app.yaml")));
484    }
485
486    /// Test merge of two property sources
487    /// 测试两个属性源的合并
488    #[test]
489    fn test_merge() {
490        let mut source1 = PropertySource::new("source1");
491        source1.put("shared", "from_source1");
492        source1.put("only_in_1", "value1");
493
494        let mut source2 = PropertySource::new("source2");
495        source2.put("shared", "from_source2");
496        source2.put("only_in_2", "value2");
497
498        source1.merge(source2);
499        // Merged source should have 3 keys
500        assert_eq!(source1.len(), 3);
501        // source2's "shared" overwrites source1's "shared"
502        assert_eq!(source1.get("shared").unwrap().as_str(), Some("from_source2"));
503        assert_eq!(source1.get("only_in_1").unwrap().as_str(), Some("value1"));
504        assert_eq!(source1.get("only_in_2").unwrap().as_str(), Some("value2"));
505    }
506
507    /// Test Default trait for PropertySource
508    /// 测试PropertySource的Default trait
509    #[test]
510    fn test_default() {
511        let source = PropertySource::default();
512        assert_eq!(source.name(), "default");
513        assert!(source.is_empty());
514    }
515
516    // ============================================================
517    // PropertySourceBuilder tests / 属性源构建器测试
518    // ============================================================
519
520    /// Test PropertySourceBuilder fluent API
521    /// 测试PropertySourceBuilder流畅API
522    #[test]
523    fn test_builder_basic() {
524        let source = PropertySourceBuilder::new("built-source")
525            .source_type(PropertySourceType::CommandLine)
526            .order(50)
527            .file_path(PathBuf::from("/tmp/config"))
528            .build();
529
530        assert_eq!(source.name(), "built-source");
531        assert_eq!(source.source_type(), PropertySourceType::CommandLine);
532        assert_eq!(source.order(), 50);
533        assert_eq!(source.file_path(), Some(&PathBuf::from("/tmp/config")));
534    }
535
536    /// Test PropertySourceBuilder put and put_all
537    /// 测试PropertySourceBuilder的put和put_all方法
538    #[test]
539    fn test_builder_put_properties() {
540        let mut builder = PropertySourceBuilder::new("props");
541        builder.put("key1", "value1");
542        builder.put("key2", 42);
543
544        let mut extra = HashMap::new();
545        extra.insert("key3".to_string(), Value::bool(true));
546        builder.put_all(extra);
547
548        let source = builder.build();
549        assert_eq!(source.len(), 3);
550        assert_eq!(source.get("key1").unwrap().as_str(), Some("value1"));
551        assert_eq!(source.get("key2").unwrap().as_i64(), Some(42));
552        assert_eq!(source.get("key3").unwrap().as_bool(), Some(true));
553    }
554}