parsers/
json_parser.rs

1use crate::TError;
2
3#[derive(Debug)]
4pub struct JsonSolver {
5    pub expression: Vec<String>,
6    pub pretty: bool,
7    pub recursive: bool,
8    pub json_line: bool,
9    pub skip_empty: bool,
10    pub skip_keys: Vec<String>,
11}
12
13impl From<&clap::ArgMatches<'_>> for JsonSolver {
14    fn from(input: &clap::ArgMatches<'_>) -> JsonSolver {
15        let expression = input
16            .value_of("expression")
17            // TODO: I am pretty sure its perfectly legal to use "." as a value key in JSON?
18            .map(|s| s.split('.').map(String::from).collect::<_>())
19            .unwrap_or_default();
20        let pretty = input.is_present("pretty");
21        let recursive = input.is_present("recursive");
22        let json_line = input.is_present("json-lines");
23        let skip_empty = input.is_present("skip-empty");
24        let skip_keys = input
25            .values_of("skip-key")
26            .unwrap_or_default()
27            .map(|s| s.to_string())
28            .collect();
29        JsonSolver {
30            expression,
31            pretty,
32            recursive,
33            json_line,
34            skip_empty,
35            skip_keys,
36        }
37    }
38}
39
40impl JsonSolver {
41    pub fn resolve_value_stream<R>(&self, value: R) -> Result<(), TError>
42    where
43        R: std::io::BufRead,
44    {
45        value
46            .lines()
47            .map(|value| value.map(|v| self.resolve_value_impl(&v)))
48            .flatten()
49            .flatten()
50            .flatten()
51            .map(|s| JsonSolver::value_to_string(self.pretty, &s))
52            .for_each(|s| println!("{}", s));
53        Ok(())
54    }
55
56    pub fn resolve_value(&self, value: &str) -> Result<Vec<String>, TError> {
57        Ok(value
58            .split('\n')
59            .filter(|v| !v.is_empty())
60            .map(|value| self.resolve_value_impl(value))
61            .collect::<Result<Vec<_>, _>>()?
62            .into_iter()
63            .flatten()
64            .map(|s| JsonSolver::value_to_string(self.pretty, &s))
65            .collect::<Vec<String>>())
66    }
67
68    pub fn resolve_line(&self, value: &str) -> Result<Vec<String>, TError> {
69        let result = self.resolve_value_impl(value)?;
70        Ok(result
71            .iter()
72            .map(|v| JsonSolver::value_to_string(self.pretty, v))
73            .collect())
74    }
75
76    fn resolve_value_impl(&self, value: &str) -> Result<Vec<serde_json::Value>, TError> {
77        let root = serde_json::from_str::<serde_json::Value>(value)
78            .map(|s| self.recursively_parse(s).map(|s| self.remove_keys(s)))??;
79        let resolved_value = {
80            let mut result = vec![root];
81            for expr in &self.expression {
82                result =
83                    result
84                        .into_iter()
85                        .map(
86                            |reader| -> Box<
87                                dyn Iterator<Item = Result<Option<serde_json::Value>, TError>>,
88                            > {
89                                match reader {
90                                    serde_json::Value::Array(v) => {
91                                        let next = v.into_iter().map(|values| {
92                                            let result = values.get(expr.as_str()).cloned();
93                                            if let Some(v) = result {
94                                                Ok(Some(v))
95                                            } else if self.skip_empty {
96                                                Ok(None)
97                                            } else {
98                                                Err(TError::KeyNotExist(expr.clone()))
99                                            }
100                                        });
101                                        Box::new(next)
102                                    }
103                                    o => {
104                                        let next = std::iter::once({
105                                            let result = o.get(expr.as_str()).cloned();
106                                            if let Some(v) = result {
107                                                Ok(Some(v))
108                                            } else if self.skip_empty {
109                                                Ok(None)
110                                            } else {
111                                                Err(TError::KeyNotExist(expr.clone()))
112                                            }
113                                        });
114                                        Box::new(next)
115                                    }
116                                }
117                            },
118                        )
119                        .flatten()
120                        .collect::<Result<Vec<_>, _>>()?
121                        .into_iter()
122                        .flatten()
123                        .collect::<Vec<_>>();
124            }
125            result
126        };
127        Ok(resolved_value)
128    }
129
130    fn recursively_parse(&self, value: serde_json::Value) -> Result<serde_json::Value, TError> {
131        if self.recursive {
132            match value {
133                serde_json::Value::Array(v) => {
134                    let result = v
135                        .into_iter()
136                        .map(|s| self.recursively_parse(s))
137                        .collect::<Result<Vec<_>, _>>()?;
138                    Ok(serde_json::Value::Array(result))
139                }
140                serde_json::Value::String(s) => {
141                    Ok(serde_json::from_str(&s).unwrap_or(serde_json::Value::String(s)))
142                }
143                serde_json::Value::Object(map) => {
144                    let result = map
145                        .into_iter()
146                        .map(|(key, value)| {
147                            self.recursively_parse(value)
148                                .map(|parsed_value| (key, parsed_value))
149                        })
150                        .collect::<Result<serde_json::Map<_, _>, _>>()?;
151                    Ok(serde_json::Value::Object(result))
152                }
153                v => Ok(v),
154            }
155        } else {
156            Ok(value)
157        }
158    }
159
160    fn remove_keys(&self, value: serde_json::Value) -> serde_json::Value {
161        match value {
162            serde_json::Value::Object(map) => {
163                let result = map
164                    .into_iter()
165                    .filter(|s| !self.skip_keys.contains(&s.0))
166                    .collect::<serde_json::Map<_, _>>();
167                serde_json::Value::Object(result)
168            }
169            v => v,
170        }
171    }
172
173    fn value_to_string(pretty: bool, value: &serde_json::Value) -> String {
174        if pretty {
175            serde_json::to_string_pretty(value).unwrap()
176        } else {
177            serde_json::to_string(value).unwrap()
178        }
179    }
180}
181
182pub fn clap_app() -> clap::App<'static, 'static> {
183    clap::App::new("json")
184        .about("Perform queries on JSON files")
185        .arg(
186            clap::Arg::with_name("expression")
187                .long("expression")
188                .help("Expression to evaluate the input with")
189                .takes_value(true),
190        )
191        .arg(
192            clap::Arg::with_name("pretty")
193                .long("pretty")
194                .help("Pretty prints the output")
195                .takes_value(false),
196        )
197        .arg(
198            clap::Arg::with_name("recursive")
199                .long("recursive")
200                .help(
201                    "Recursively parses every string values as JSON. \
202                Fallback to string if it fails",
203                )
204                .takes_value(false),
205        )
206        .arg(
207            clap::Arg::with_name("json-lines")
208                .long("json-lines")
209                .help("Enable JSON-lines mode")
210                .takes_value(false),
211        )
212        .arg(
213            clap::Arg::with_name("skip-empty")
214                .long("skip-empty")
215                .help("If expression fails to resolve, skip the value")
216                .takes_value(false),
217        )
218        .arg(
219            clap::Arg::with_name("skip-key")
220                .long("skip-key")
221                .help(
222                    "Keys to be removed from the final output. \
223                    Currently only supports removing root keys.",
224                )
225                .takes_value(true)
226                .multiple(true),
227        )
228        .author(clap::crate_authors!())
229}