dev_tool/
conf_util_yaml.rs

1use crate::FileUtil;
2use serde_yaml::Value;
3use std::path::Path;
4
5#[derive(Debug)]
6pub struct YamlWrapper {
7    pub file_path: String,
8    pub raw_data: String,
9    pub data: Value,
10}
11
12pub struct ValueWrapper<'a> {
13    value: &'a Value,
14}
15
16impl<'a> ValueWrapper<'a> {
17    /// 创建一个新的实例
18    ///
19    /// # 参数
20    /// * `value` - 指向Value的引用
21    ///
22    /// # 返回值
23    /// 返回包含指定value的新实例
24    pub fn new(value: &'a Value) -> Self {
25        Self { value }
26    }
27
28    /// 根据键名获取对应的值
29    ///
30    /// 该函数支持通过点号分隔的多层键名来访问嵌套的数据结构,
31    /// 也支持在数组中查找指定键名的值。
32    ///
33    /// # 参数
34    /// * `key` - 要查找的键名,可以是单层键名或点号分隔的多层键名
35    ///
36    /// # 返回值
37    /// 返回包含查找到的值的向量,如果未找到则包含Null值
38    pub fn get(&self, key: &'a str) -> Vec<&'a Value> {
39        let mut results = Vec::new();
40        if let Some((left, right)) = key.split_once(".") {
41            // 处理多层键名的情况
42            let x = self.value.get(left);
43            if x.is_none() {
44                results.push(&Value::Null);
45            } else {
46                let x = x.unwrap();
47                let next = ValueWrapper::new(x);
48                results.append(&mut next.get(right));
49            }
50        } else {
51            // 处理单层键名的情况
52            match self.value {
53                Value::Mapping(map) => {
54                    // 在对象中查找键名
55                    if let Some(v) = map.get(key) {
56                        results.push(v);
57                    } else {
58                        results.push(&Value::Null);
59                    }
60                }
61                Value::Sequence(vec) => {
62                    // 在数组的每个元素中查找键名
63                    for item in vec {
64                        match item.get(key) {
65                            Some(v) => results.push(v),
66                            None => results.push(&Value::Null),
67                        }
68                    }
69                }
70                _ => results.push(&Value::Null),
71            }
72        }
73        results
74    }
75}
76
77impl YamlWrapper {
78    /// 创建一个新的配置实例
79    ///
80    /// 该函数会读取指定路径的配置文件,解析JSON格式的内容,并创建配置对象
81    ///
82    /// # 参数
83    /// * `file_path` - 配置文件的路径字符串引用
84    ///
85    /// # 返回值
86    /// 返回Result类型,成功时包含配置实例,失败时包含IO错误
87    ///
88    /// # 错误
89    /// 当文件读取失败或JSON解析失败时会返回相应的错误
90    pub fn new(file_path: &str) -> Result<Self, Box<dyn std::error::Error>> {
91        // 读取配置文件内容
92        let conf_string = FileUtil::read_string(Path::new(file_path))?;
93        // 解析JSON数据
94        let data: Value = serde_yaml::from_str(&conf_string)?;
95        Ok(Self {
96            file_path: file_path.to_string(),
97            raw_data: conf_string,
98            data,
99        })
100    }
101
102    /// 获取指定键对应的值列表
103    ///
104    /// 该函数通过创建一个值包装器来访问内部数据,并返回与给定键关联的所有值。
105    ///
106    /// # 参数
107    /// * `key` - 要查找的静态字符串键
108    ///
109    /// # 返回值
110    /// 返回一个包含所有匹配值的引用向量
111    pub fn get<'a, 'b: 'a>(&'a self, key: &'b str) -> Vec<&'a Value> {
112        let wrapper = ValueWrapper::new(&self.data);
113        wrapper.get(key)
114    }
115
116    /// 获取指定键对应的第一个值
117    ///
118    /// # 参数
119    /// * `key` - 要查找的键名,必须是静态字符串
120    ///
121    /// # 返回值
122    /// 返回键对应值的引用
123    ///
124    /// # 注意
125    /// 如果键不存在或对应的值为空,会触发panic
126    pub fn get_one<'a, 'b: 'a>(&'a self, key: &'b str) -> &'a Value {
127        // 该方法注释,要求b的生命周期不小于a的生命周期
128        // 创建值包装器来处理数据查询
129        let wrapper = ValueWrapper::new(&self.data);
130        // 获取指定键对应的值向量
131        let vec = wrapper.get(key);
132        // 返回向量中的第一个值,如果不存在则panic
133        vec.first().unwrap()
134    }
135}
136
137#[cfg(test)]
138mod tests {
139
140    use super::*;
141
142    #[test]
143    fn test_json_conf_util() {
144        
145        let wrapper = YamlWrapper::new("docs/config.yaml").unwrap();
146
147        let x = wrapper.get("name");
148        // name = [String("Alice")]
149        println!("name = {:?}", x);
150
151        let x = wrapper.get("age");
152        // age = [Number(30)]
153        println!("age = {:?}", x);
154
155        let x = wrapper.get("is_student");
156        // is_student = [Bool(false)]
157        println!("is_student = {:?}", x);
158
159        let x = wrapper.get("hobbies");
160        // hobbies = [Sequence [String("Reading"), String("Hiking"), String("Cooking")]]
161        println!("hobbies = {:?}", x);
162
163        let x = wrapper.get("address");
164        // address = [Mapping {"street": String("123 Main St"), "city": String("Anytown"), "state": String("CA"), "zip": String("10001"), "releases": Sequence [String("v1"), String("v2")], "x": Mapping {"y": String("hello, json!")}, "children": Sequence [Mapping {"name": String("r"), "age": Number(5)}, Mapping {"name": String("s"), "age": Number(6)}]}]
165        println!("address = {:?}", x);
166
167        let x = wrapper.get("address.street");
168        // address.street = [String("123 Main St")]
169        println!("address.street = {:?}", x);
170
171        let x = wrapper.get("address.releases");
172        // address.releases = [Sequence [String("v1"), String("v2")]]
173        println!("address.releases = {:?}", x);
174
175        let x = wrapper.get("address.x.y");
176        // address.x.y = [String("hello, json!")]
177        println!("address.x.y = {:?}", x);
178
179        let x = wrapper.get("address.children.name");
180        // address.children.name = [String("r"), String("s")]
181        println!("address.children.name = {:?}", x);
182        println!("=============================================================");
183
184        let name = wrapper.get_one("name").as_str().unwrap();
185        let age = wrapper.get_one("age").as_i64().unwrap();
186        println!("name = {:?}", name);
187        println!("age = {:?}", age);
188
189        let key = String::from("address.x.y");
190        let x = wrapper.get_one(&key).as_str().unwrap();
191        // address.x.y = hello, json!
192        println!("address.x.y = {}", x);
193
194    }
195}