1use std::fmt;
4use std::io::{self, Read, Write};
5
6use serde_json::Value;
7
8use crate::tree::{ActionValues, NodeId, Tree};
9
10const MAX_OBJECT_PREVIEW_BYTES: usize = 512;
13const MAX_OBJECT_PREVIEW_MEMBERS: usize = 32;
15
16#[derive(Debug)]
17pub struct Error(serde_json::Error);
18
19impl fmt::Display for Error {
20 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
21 write!(formatter, "invalid JSON input: {}", self.0)
22 }
23}
24
25impl std::error::Error for Error {
26 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
27 Some(&self.0)
28 }
29}
30
31pub fn from_reader(reader: impl Read) -> Result<Tree, Error> {
32 let value = serde_json::from_reader(reader).map_err(Error)?;
33 Ok(transform(&value))
34}
35
36fn transform(value: &Value) -> Tree {
37 let mut tree = Tree::new();
38 match value {
39 Value::Object(members) if !members.is_empty() => {
40 for (key, value) in members {
41 push_value(&mut tree, None, key, &append_pointer("", key), value);
42 }
43 }
44 _ => {
45 push_value(&mut tree, None, "$", "", value);
46 }
47 }
48 tree
49}
50
51fn push_value(
52 tree: &mut Tree,
53 parent: Option<NodeId>,
54 prefix: &str,
55 pointer: &str,
56 value: &Value,
57) -> NodeId {
58 let (name, detail) = label(prefix, value);
59 let alternate_output =
60 serde_json::to_string(value).expect("serializing a JSON value cannot fail");
61 let action =
62 ActionValues::new(pointer, pointer, pointer).with_alternate_output(alternate_output);
63 let id = tree.push_with_detail(
64 parent,
65 name,
66 detail,
67 matches!(value, Value::Array(_) | Value::Object(_)),
68 action,
69 );
70
71 match value {
72 Value::Array(elements) => {
73 for (index, value) in elements.iter().enumerate() {
74 let index = index.to_string();
75 push_value(
76 tree,
77 Some(id),
78 &format!("[{index}]"),
79 &append_pointer(pointer, &index),
80 value,
81 );
82 }
83 }
84 Value::Object(members) => {
85 for (key, value) in members {
86 push_value(tree, Some(id), key, &append_pointer(pointer, key), value);
87 }
88 }
89 _ => {}
90 }
91 id
92}
93
94fn label(prefix: &str, value: &Value) -> (String, Option<String>) {
95 match value {
96 Value::Array(elements) if elements.is_empty() => (format!("{prefix} []"), None),
97 Value::Array(elements) => (format!("{prefix} [{}]", elements.len()), None),
98 Value::Object(members) if members.is_empty() => (format!("{prefix} {{}}"), None),
99 Value::Object(members) => (
100 format!("{prefix} {{{}}}", members.len()),
101 object_preview(members),
102 ),
103 _ => (
104 format!(
105 "{prefix}: {}",
106 scalar(value).expect("non-container JSON values are scalar")
107 ),
108 None,
109 ),
110 }
111}
112
113fn object_preview(members: &serde_json::Map<String, Value>) -> Option<String> {
114 let mut preview = Preview::new(MAX_OBJECT_PREVIEW_BYTES);
115 for (key, value) in members.iter().take(MAX_OBJECT_PREVIEW_MEMBERS) {
116 if !matches!(
117 value,
118 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_)
119 ) {
120 continue;
121 }
122 if !preview.is_empty() && !preview.push_str(" 路 ") {
123 break;
124 }
125 if !preview.push_str(key) || !preview.push_str(": ") {
126 break;
127 }
128 preview.push_json(value);
129 if preview.is_exhausted() {
130 break;
131 }
132 }
133 preview.finish()
134}
135
136struct Preview {
137 text: String,
138 limit: usize,
139 exhausted: bool,
140}
141
142impl Preview {
143 fn new(limit: usize) -> Self {
144 Self {
145 text: String::with_capacity(limit),
146 limit,
147 exhausted: false,
148 }
149 }
150
151 fn is_empty(&self) -> bool {
152 self.text.is_empty()
153 }
154
155 fn is_exhausted(&self) -> bool {
156 self.exhausted
157 }
158
159 fn push_str(&mut self, text: &str) -> bool {
160 if self.exhausted {
161 return false;
162 }
163 let remaining = self.limit.saturating_sub(self.text.len());
164 if text.len() <= remaining {
165 self.text.push_str(text);
166 return true;
167 }
168
169 let mut end = remaining;
170 while !text.is_char_boundary(end) {
171 end -= 1;
172 }
173 self.text.push_str(&text[..end]);
174 self.exhausted = true;
175 false
176 }
177
178 fn push_json(&mut self, value: &Value) {
179 if serde_json::to_writer(&mut *self, value).is_err() {
180 self.exhausted = true;
181 }
182 }
183
184 fn finish(self) -> Option<String> {
185 (!self.text.is_empty()).then_some(self.text)
186 }
187}
188
189impl Write for Preview {
190 fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
191 if bytes.is_empty() {
192 return Ok(0);
193 }
194 if self.exhausted {
195 return Err(io::ErrorKind::WriteZero.into());
196 }
197
198 let remaining = self.limit.saturating_sub(self.text.len());
199 let candidate = &bytes[..bytes.len().min(remaining)];
200 let end = std::str::from_utf8(candidate).map_or_else(|error| error.valid_up_to(), str::len);
201 if end == 0 {
202 self.exhausted = true;
203 return Err(io::ErrorKind::WriteZero.into());
204 }
205
206 self.text.push_str(
207 std::str::from_utf8(&candidate[..end])
208 .expect("a prefix ending on a UTF-8 boundary is valid"),
209 );
210 self.exhausted = end < bytes.len();
211 Ok(end)
212 }
213
214 fn flush(&mut self) -> io::Result<()> {
215 Ok(())
216 }
217}
218
219fn scalar(value: &Value) -> Option<String> {
220 match value {
221 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {
222 Some(serde_json::to_string(value).expect("serializing a JSON scalar cannot fail"))
223 }
224 Value::Array(_) | Value::Object(_) => None,
225 }
226}
227
228fn append_pointer(parent: &str, token: &str) -> String {
229 let token = token.replace('~', "~0").replace('/', "~1");
230 format!("{parent}/{token}")
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236 use std::ffi::OsStr;
237
238 const DEMO_JSON: &str = include_str!("../examples/sample.json");
239
240 fn parse(json: &str) -> Tree {
241 from_reader(json.as_bytes()).unwrap()
242 }
243
244 fn names<'a>(tree: &'a Tree, ids: &[usize]) -> Vec<&'a str> {
245 ids.iter().map(|&id| tree.node(id).name.as_str()).collect()
246 }
247
248 #[test]
249 fn object_members_become_ordered_roots_with_container_sizes_and_scalar_values() {
250 let tree = parse(
251 r#"{
252 "users": [{"id": 12, "name": "Ada"}, {"id": 27}, null],
253 "empty": [],
254 "settings": {},
255 "enabled": true
256 }"#,
257 );
258
259 assert_eq!(
260 names(&tree, tree.root_ids()),
261 ["users [3]", "empty []", "settings {}", "enabled: true"]
262 );
263 assert!(tree.node(tree.root_ids()[0]).is_container);
264 assert!(tree.node(tree.root_ids()[1]).is_container);
265 assert!(tree.is_leaf(tree.root_ids()[1]));
266 }
267
268 #[test]
269 fn array_elements_keep_their_order_and_include_object_previews() {
270 let tree = parse(r#"["rust", 7, {"id": 12, "name": "Ada"}, [null]]"#);
271 let root = tree.root_ids()[0];
272 let object = tree.node(root).children[2];
273
274 assert_eq!(tree.node(root).name, "$ [4]");
275 assert_eq!(
276 names(&tree, &tree.node(root).children),
277 ["[0]: \"rust\"", "[1]: 7", "[2] {2}", "[3] [1]"]
278 );
279 assert_eq!(
280 tree.node(object).detail.as_deref(),
281 Some(r#"id: 12 路 name: "Ada""#)
282 );
283 }
284
285 #[test]
286 fn object_previews_include_more_than_two_scalar_members() {
287 let tree = parse(r#"{"item":{"a":1,"b":2,"c":3}}"#);
288 let item = tree.root_ids()[0];
289
290 assert_eq!(
291 tree.node(item).detail.as_deref(),
292 Some("a: 1 路 b: 2 路 c: 3")
293 );
294 }
295
296 #[test]
297 fn object_previews_are_bounded_and_remain_valid_utf8() {
298 let json = format!(r#"{{"item":{{"huge":"{}"}}}}"#, "馃榾".repeat(1_000));
299 let tree = parse(&json);
300 let item = tree.root_ids()[0];
301 let preview = tree.node(item).detail.as_deref().unwrap();
302
303 assert!(preview.len() <= 512, "preview used {} bytes", preview.len());
304 assert!(preview.starts_with(r#"huge: ""#));
305 }
306
307 #[test]
308 fn object_previews_inspect_at_most_the_first_32_members() {
309 let mut object = serde_json::Map::new();
310 for index in 0..32 {
311 object.insert(format!("nested-{index}"), Value::Array(Vec::new()));
312 }
313 object.insert("too-late".to_owned(), Value::Bool(true));
314 let document = Value::Object(
315 [("item".to_owned(), Value::Object(object))]
316 .into_iter()
317 .collect(),
318 );
319 let tree = transform(&document);
320 let item = tree.root_ids()[0];
321
322 assert_eq!(tree.node(item).detail, None);
323 }
324
325 #[test]
326 fn every_node_outputs_its_canonical_json_pointer() {
327 let tree = parse(r#"["rust", {"a/b": {"~key": "value"}}]"#);
328 let root = tree.root_ids()[0];
329 let text = tree.node(root).children[0];
330 let object = tree.node(root).children[1];
331 let slash_key = tree.node(object).children[0];
332 let tilde_key = tree.node(slash_key).children[0];
333
334 assert_eq!(tree.node(root).action.output, OsStr::new(""));
335 assert_eq!(
336 tree.node(root).action.alternate_output,
337 OsStr::new(r#"["rust",{"a/b":{"~key":"value"}}]"#)
338 );
339 assert_eq!(tree.node(root).action.path, OsStr::new(""));
340 assert_eq!(tree.node(text).action.output, OsStr::new("/0"));
341 assert_eq!(
342 tree.node(text).action.alternate_output,
343 OsStr::new(r#""rust""#)
344 );
345 assert_eq!(tree.node(text).action.path, OsStr::new("/0"));
346 assert_eq!(tree.node(slash_key).action.output, OsStr::new("/1/a~1b"));
347 assert_eq!(tree.node(slash_key).action.path, OsStr::new("/1/a~1b"));
348 assert_eq!(
349 tree.node(tilde_key).action.path,
350 OsStr::new("/1/a~1b/~0key")
351 );
352 assert_eq!(
353 tree.node(tilde_key).action.relpath,
354 tree.node(tilde_key).action.path
355 );
356 assert_eq!(
357 tree.node(tilde_key).action.output,
358 OsStr::new("/1/a~1b/~0key")
359 );
360 assert_eq!(
361 tree.node(tilde_key).action.alternate_output,
362 OsStr::new(r#""value""#)
363 );
364 }
365
366 #[test]
367 fn scalar_and_empty_object_roots_remain_selectable() {
368 let scalar = parse("null");
369 assert_eq!(names(&scalar, scalar.root_ids()), ["$: null"]);
370 assert_eq!(
371 scalar.node(scalar.root_ids()[0]).action.output,
372 OsStr::new("")
373 );
374
375 let empty = parse("{}");
376 assert_eq!(names(&empty, empty.root_ids()), ["$ {}"]);
377 assert_eq!(
378 empty.node(empty.root_ids()[0]).action.output,
379 OsStr::new("")
380 );
381 assert!(empty.node(empty.root_ids()[0]).is_container);
382 assert!(empty.is_leaf(empty.root_ids()[0]));
383 }
384
385 #[test]
386 fn invalid_json_is_reported() {
387 let error = from_reader("{]".as_bytes()).unwrap_err();
388 assert!(error.to_string().starts_with("invalid JSON input:"));
389 }
390
391 #[test]
392 fn demo_sample_exercises_the_json_tree_shapes() {
393 let tree = parse(DEMO_JSON);
394
395 assert_eq!(
396 names(&tree, tree.root_ids()),
397 ["project {4}", "users [3]", "settings {}", "version: 1"]
398 );
399 assert_eq!(
400 tree.node(tree.root_ids()[0]).detail.as_deref(),
401 Some(r#"name: "ite" 路 status: "experimental""#)
402 );
403 let users = tree.root_ids()[1];
404 assert_eq!(tree.node(users).children.len(), 3);
405 assert_eq!(tree.node(tree.node(users).children[2]).name, "[2]: null");
406 }
407}