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}