dev_kit/command/json/
json.rs

1use super::{DiffTool, Json, KeyPatternType, QueryType};
2use itertools::Itertools;
3use jsonpath_rust::JsonPath;
4use serde::ser::Error;
5use serde::{Serialize, Serializer};
6use serde_json::Value;
7use std::collections::BTreeMap;
8use std::ops::Deref;
9use std::sync::Arc;
10use std::{env, fs};
11
12impl Json {
13    pub fn search_paths(&self, query: Option<&str>, query_type: Option<QueryType>) -> crate::Result<Vec<Jsonpath>> {
14        let json = Arc::<Value>::try_from(self)?;
15        let query = query.map(|it| it.trim()).filter(|it| it.len() > 0).unwrap_or("");
16        let (kp, qt) = Self::parse_query_type(query, query_type)?;
17        match (kp, qt, query, query.is_empty()) {
18            (_, _, _, true) => {
19                Ok(vec![])
20            }
21            (Some(key_pattern), _, _, false) => {
22                Ok(Self::search_key_actual(&json, &key_pattern, None))
23            }
24            (None, Some(QueryType::JsonPath), query, false) => {
25                let (prefix, keyword) = if let Some((prefix, keyword)) = query.rsplit_once(".") {
26                    (prefix, Some(keyword))
27                } else {
28                    (query, None)
29                };
30                match (prefix, keyword, keyword.unwrap_or("").is_empty()){
31                    (prefix, Some(keyword), false) => {
32                        Ok(Self::search_key_actual(&json, &KeyPattern::Prefix(keyword.to_lowercase()), Some(prefix)))
33                    }
34                    (prefix,_,_) => {
35                        Ok(json.query(prefix)?.iter().flat_map(|it| match it {
36                            Value::Object(map) => map.keys().map(|k| k.to_string()).collect_vec(),
37                            Value::Array(_) => vec!["*".to_string()],
38                            _ => vec![],
39                        }).unique().map(|it| Jsonpath(it)).collect_vec())
40                    }
41                }
42            }
43            _ => {
44                unreachable!()
45            }
46        }
47    }
48
49    pub fn query(&self, query: Option<&str>, query_type: Option<QueryType>, beauty: bool) -> crate::Result<String> {
50        let json = Arc::<Value>::try_from(self)?;
51        let query_vals = Self::query_actual(&json, query, query_type)?;
52        if beauty {
53            Ok(serde_json::to_string_pretty(&query_vals)?)
54        } else {
55            Ok(serde_json::to_string(&query_vals)?)
56        }
57    }
58
59    pub fn diff(
60        &self,
61        other: &Self,
62        query: Option<&str>,
63        query_type: Option<QueryType>,
64        diff_tool: Option<DiffTool>,
65    ) -> crate::Result<()> {
66        let tmp_dir = env::temp_dir()
67            .join("jsondiff")
68            .join(chrono::Local::now().format("%Y%m%d%H%M%S%f").to_string());
69        if tmp_dir.exists() {
70            fs::remove_dir_all(&tmp_dir)?;
71        }
72        let left = self;
73        let right = other;
74        let _ = fs::create_dir_all(&tmp_dir)?;
75        let left = left.diff_prepare(query.as_deref(), query_type)?;
76        let left_path = tmp_dir.join("left.json");
77        fs::write(&left_path, left)?;
78        println!("write left to file {}", left_path.display());
79        let right = right.diff_prepare(query.as_deref(), query_type)?;
80        let right_path = tmp_dir.join("right.json");
81        fs::write(&right_path, right)?;
82        println!("write right to file {}", right_path.display());
83        let diff_tool = diff_tool.unwrap_or_default();
84        if diff_tool.is_available() {
85            println!("diff with {}", diff_tool);
86            diff_tool.diff(&left_path, &right_path)?;
87        } else {
88            eprintln!("diff tool {} is not installed", diff_tool);
89            println!(
90                r#"
91install {} command-line interface, see:
92{}"#,
93                diff_tool,
94                diff_tool.how_to_install()
95            )
96        }
97        Ok(())
98    }
99}
100
101#[derive(Debug, Clone)]
102enum QueryVals {
103    Origin(Value),
104    JsonPath(Vec<Value>),
105    KeyPattern(BTreeMap<Jsonpath, Vec<Value>>),
106}
107
108impl serde::Serialize for QueryVals {
109    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110    where
111        S: Serializer,
112    {
113        match self {
114            QueryVals::Origin(vals) => {
115                vals.serialize(serializer)
116            }
117            QueryVals::JsonPath(vals) => {
118                match serde_json::to_value(vals) {
119                    Ok(v) => v.serialize(serializer),
120                    Err(err) => Err(S::Error::custom(format!("{}", err)))
121                }
122            }
123            QueryVals::KeyPattern(vals) => {
124                match serde_json::to_value(vals) {
125                    Ok(v) => v.serialize(serializer),
126                    Err(err) => Err(S::Error::custom(format!("{}", err)))
127                }
128            }
129        }
130    }
131}
132
133#[derive(Debug, Clone)]
134enum KeyPattern {
135    Prefix(String),
136    Suffix(String),
137    Contains(String),
138    Regex(regex::Regex),
139}
140
141impl From<&KeyPattern> for KeyPatternType {
142    fn from(value: &KeyPattern) -> Self {
143        match value {
144            KeyPattern::Prefix(_) => KeyPatternType::Prefix,
145            KeyPattern::Suffix(_) => KeyPatternType::Suffix,
146            KeyPattern::Contains(_) => KeyPatternType::Contains,
147            KeyPattern::Regex(_) => KeyPatternType::Regex,
148        }
149    }
150}
151
152impl KeyPattern {
153    fn new(key_pattern: &str, pattern_type: KeyPatternType) -> crate::Result<Self> {
154        match pattern_type {
155            KeyPatternType::Prefix => Ok(Self::Prefix(key_pattern.to_lowercase())),
156            KeyPatternType::Suffix => Ok(Self::Suffix(key_pattern.to_lowercase())),
157            KeyPatternType::Contains => Ok(Self::Contains(key_pattern.to_lowercase())),
158            KeyPatternType::Regex => Ok(Self::Regex(
159                regex::RegexBuilder::new(key_pattern).case_insensitive(true).build()?
160            )),
161        }
162    }
163
164    fn guess(key_pattern: &str) -> crate::Result<Self> {
165        match regex::RegexBuilder::new(key_pattern).case_insensitive(true).build() {
166            Ok(regex) => {
167                Ok(Self::Regex(regex))
168            }
169            Err(_) => {
170                Self::new(key_pattern, KeyPatternType::default())
171            }
172        }
173    }
174
175    fn match_key(&self, key: &str) -> bool {
176        match self {
177            KeyPattern::Prefix(prefix) => {
178                let key = key.to_lowercase();
179                key.starts_with(prefix)
180            }
181            KeyPattern::Suffix(suffix) => {
182                let key = key.to_lowercase();
183                key.ends_with(suffix)
184            }
185            KeyPattern::Contains(contains) => {
186                let key = key.to_lowercase();
187                key.contains(contains)
188            }
189            KeyPattern::Regex(regex) => {
190                regex.is_match(key)
191            }
192        }
193    }
194}
195
196impl Json {
197    fn parse_query_type(
198        query: &str,
199        query_type: Option<QueryType>,
200    ) -> crate::Result<(Option<KeyPattern>, Option<QueryType>)> {
201        match (query_type, query.is_empty(), query.starts_with("$")) {
202            (Some(QueryType::JsonPath), _, _) | (None, false, true) => {
203                Ok((None, Some(QueryType::JsonPath)))
204            }
205            (Some(qt @ QueryType::KeyPattern(kpt)), _, _) => {
206                Ok((Some(KeyPattern::new(query, kpt)?), Some(qt)))
207            }
208            (None, true, _) => {
209                Ok((None, None))
210            }
211            (None, false, false) => {
212                let kp = KeyPattern::guess(query).ok();
213                let qt = kp.as_ref().map(|it|
214                    KeyPatternType::from(it)
215                ).map(|kpt|
216                    QueryType::KeyPattern(kpt)
217                );
218                Ok((kp, qt))
219            }
220        }
221    }
222
223    fn query_actual(
224        json: &Value,
225        query: Option<&str>,
226        query_type: Option<QueryType>,
227    ) -> crate::Result<QueryVals> {
228        let query = query.map(|it| it.trim()).filter(|it| it.len() > 0).unwrap_or("");
229        let (kp, qt) = Self::parse_query_type(query, query_type)?;
230        match (kp, qt, query, query.is_empty()) {
231            (_, _, _, true) => {
232                Ok(QueryVals::Origin(json.to_owned()))
233            }
234            (Some(key_pattern), _, _, false) => {
235                let json_paths = Self::search_key_actual(&json, &key_pattern, None);
236                let mut map = BTreeMap::new();
237                for path in json_paths.into_iter() {
238                    let arr = json.query(&path).into_iter().flatten().map(|it| it.to_owned()).collect_vec();
239                    let _ = map.insert(path, arr);
240                }
241                Ok(QueryVals::KeyPattern(map))
242            }
243            (None, Some(QueryType::JsonPath), query, false) => {
244                let query = query.trim_end_matches(".");
245                let arr = json.query(&query).into_iter().flatten().map(|it| it.to_owned()).collect_vec();
246                Ok(QueryVals::JsonPath(arr))
247            }
248            _ => {
249                unreachable!()
250            }
251        }
252    }
253
254    fn diff_prepare(&self, query: Option<&str>, query_type: Option<QueryType>) -> crate::Result<String> {
255        let json = Arc::<Value>::try_from(self)?;
256        let array = Self::query_actual(&json, query, query_type)?;
257        let pretty = serde_json::to_string_pretty(&array)?;
258        Ok(pretty)
259    }
260
261    fn search_key_actual(json: &Value, key_pattern: &KeyPattern, prefix: Option<&str>) -> Vec<Jsonpath> {
262        let jsons = match &prefix {
263            Some(prefix) => {
264                match json.query(prefix) {
265                    Ok(arr) => arr,
266                    Err(_) => vec![json],
267                }
268            }
269            None => vec![json],
270        };
271        Self::search_key_recursive(&jsons, &key_pattern, prefix.unwrap_or("$")).into_iter()
272            .unique().map(|it| Jsonpath(it)).collect_vec()
273    }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
277#[serde(transparent)]
278pub struct Jsonpath(String);
279impl Deref for Jsonpath {
280    type Target = str;
281    fn deref(&self) -> &Self::Target {
282        &self.0
283    }
284}
285
286impl From<Jsonpath> for String {
287    fn from(it: Jsonpath) -> Self {
288        it.0
289    }
290}
291
292impl Json {
293    fn search_key_recursive(jsons: &[&Value], key_pattern: &KeyPattern, path: &str) -> Vec<String> {
294        jsons.iter().flat_map(|&json| {
295            match json {
296                Value::Object(map) => {
297                    let mut vec = Vec::with_capacity(map.len());
298                    for (k, v) in map {
299                        let path = format!("{}.{}", path, k);
300                        let mut children = Self::search_key_recursive(&vec![v], key_pattern, &path);
301                        if key_pattern.match_key(k) {
302                            vec.push(path);
303                        }
304                        let _ = vec.append(&mut children);
305                    }
306                    vec
307                }
308                Value::Array(array) => {
309                    let mut vec = Vec::with_capacity(array.len());
310                    let path = format!("{}.*", path);
311                    let jsons = array.iter().map(|it| it).collect_vec();
312                    let mut children = Self::search_key_recursive(&jsons, key_pattern, &path);
313                    if children.len() > 0 {
314                        let _ = vec.append(&mut children);
315                    }
316                    vec
317                }
318                _ => {
319                    vec![]
320                }
321            }
322        }).collect_vec()
323    }
324}