dev_kit/command/json/
json.rs

1use super::{DiffTool, Json, KeyPatternType, QueryType};
2use itertools::Itertools;
3use jsonpath_rust::JsonPath;
4use lazy_static::lazy_static;
5use serde::ser::Error;
6use serde::{Serialize, Serializer};
7use serde_json::Value;
8use std::collections::BTreeMap;
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<JsonpathMatch>> {
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::guess(keyword)?, 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| it.into()).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        let key_pattern = key_pattern.trim();
166        match regex::RegexBuilder::new(key_pattern).case_insensitive(true).build() {
167            Ok(regex) => {
168                Ok(Self::Regex(regex))
169            }
170            Err(_) => {
171                Self::new(key_pattern, KeyPatternType::default())
172            }
173        }
174    }
175
176    fn match_key(&self, key: &str) -> bool {
177        match self {
178            KeyPattern::Prefix(prefix) => {
179                let key = key.to_lowercase();
180                key.starts_with(prefix)
181            }
182            KeyPattern::Suffix(suffix) => {
183                let key = key.to_lowercase();
184                key.ends_with(suffix)
185            }
186            KeyPattern::Contains(contains) => {
187                let key = key.to_lowercase();
188                key.contains(contains)
189            }
190            KeyPattern::Regex(regex) => {
191                regex.is_match(key)
192            }
193        }
194    }
195}
196
197impl Json {
198    fn parse_query_type(
199        query: &str,
200        query_type: Option<QueryType>,
201    ) -> crate::Result<(Option<KeyPattern>, Option<QueryType>)> {
202        match (query_type, query.is_empty(), query.starts_with("$")) {
203            (Some(QueryType::JsonPath), _, _) | (None, false, true) => {
204                Ok((None, Some(QueryType::JsonPath)))
205            }
206            (Some(qt @ QueryType::KeyPattern(kpt)), _, _) => {
207                Ok((Some(KeyPattern::new(query, kpt)?), Some(qt)))
208            }
209            (None, true, _) => {
210                Ok((None, None))
211            }
212            (None, false, false) => {
213                let kp = KeyPattern::guess(query).ok();
214                let qt = kp.as_ref().map(|it|
215                    KeyPatternType::from(it)
216                ).map(|kpt|
217                    QueryType::KeyPattern(kpt)
218                );
219                Ok((kp, qt))
220            }
221        }
222    }
223
224    fn query_actual(
225        json: &Value,
226        query: Option<&str>,
227        query_type: Option<QueryType>,
228    ) -> crate::Result<QueryVals> {
229        let query = query.map(|it| it.trim()).filter(|it| it.len() > 0).unwrap_or("");
230        let (kp, qt) = Self::parse_query_type(query, query_type)?;
231        match (kp, qt, query, query.is_empty()) {
232            (_, _, _, true) => {
233                Ok(QueryVals::Origin(json.to_owned()))
234            }
235            (Some(key_pattern), _, _, false) => {
236                let json_paths = Self::search_key_actual(&json, &key_pattern, None);
237                let mut map = BTreeMap::new();
238                for path in json_paths.into_iter().map(|it| it.take_jsonpath()).collect_vec() {
239                    let arr = json.query(&path).into_iter().flatten().map(|it| it.to_owned()).collect_vec();
240                    let _ = map.insert(path, arr);
241                }
242                Ok(QueryVals::KeyPattern(map))
243            }
244            (None, Some(QueryType::JsonPath), query, false) => {
245                let query = query.trim_end_matches(".");
246                let arr = json.query(&query).into_iter().flatten().map(|it| it.to_owned()).collect_vec();
247                Ok(QueryVals::JsonPath(arr))
248            }
249            _ => {
250                unreachable!()
251            }
252        }
253    }
254
255    fn diff_prepare(&self, query: Option<&str>, query_type: Option<QueryType>) -> crate::Result<String> {
256        let json = Arc::<Value>::try_from(self)?;
257        let array = Self::query_actual(&json, query, query_type)?;
258        let pretty = serde_json::to_string_pretty(&array)?;
259        Ok(pretty)
260    }
261
262    fn search_key_actual(json: &Value, key_pattern: &KeyPattern, prefix: Option<&str>) -> Vec<JsonpathMatch> {
263        let jsons = match &prefix {
264            Some(prefix) => {
265                match json.query(prefix) {
266                    Ok(arr) => arr,
267                    Err(_) => vec![json],
268                }
269            }
270            None => vec![json],
271        };
272        Self::search_key_recursive(&jsons, &key_pattern, prefix.unwrap_or("$")).into_iter()
273            .unique().collect_vec()
274    }
275}
276
277#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
278#[serde(untagged)]
279pub enum JsonpathMatch {
280    Key(MatchKey),
281    Val(MatchVal),
282}
283
284mod jsonpath_match;
285pub use jsonpath_match::*;
286
287impl Json {
288    fn search_key_recursive(jsons: &[&Value], key_pattern: &KeyPattern, path: &str) -> Vec<JsonpathMatch> {
289        jsons.iter().flat_map(|&json| {
290            match json {
291                Value::Object(map) => {
292                    let mut vec = Vec::with_capacity(map.len());
293                    for (k, v) in map {
294                        lazy_static! {
295                            static ref START_NUM_PATTERN: regex::Regex = regex::Regex::new(r"^\d+$").unwrap();
296                        }
297                        let path = if START_NUM_PATTERN.is_match(k) {
298                            format!("{}['{}']", path, k)
299                        } else {
300                            format!("{}.{}", path, k)
301                        };
302                        let mut children = Self::search_key_recursive(&vec![v], key_pattern, &path);
303                        if key_pattern.match_key(k) {
304                            vec.push(JsonpathMatch::from(path.as_str()));
305                        }
306                        let _ = vec.append(&mut children);
307                    }
308                    vec
309                }
310                Value::Array(array) => {
311                    let mut vec = Vec::with_capacity(array.len() + 1);
312                    vec.push(format!("{}[*]", path).into());
313                    for (idx, json) in array.iter().enumerate() {
314                        let path = format!("{}[{}]", path, idx);
315                        let mut children = Self::search_key_recursive(&vec![json], key_pattern, &path);
316                        let _ = vec.append(&mut children);
317                    }
318                    if vec.len() > 1 {
319                        vec
320                    } else {
321                        vec![]
322                    }
323                }
324                v @ Value::Bool(_) | v @ Value::Number(_) => {
325                    let string_val = v.to_string();
326                    if key_pattern.match_key(&string_val) {
327                        vec![JsonpathMatch::from((path, v))]
328                    } else {
329                        vec![]
330                    }
331                }
332                v @ Value::String(string_val) => {
333                    if key_pattern.match_key(string_val) {
334                        vec![JsonpathMatch::from((path, v))]
335                    } else {
336                        vec![]
337                    }
338                }
339                _ => {
340                    vec![]
341                }
342            }
343        }).collect_vec()
344    }
345}