dev_tool/
conf_util.rs

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