1#![allow(clippy::manual_strip, clippy::should_implement_trait)]
2#![allow(dead_code)]
8
9#[derive(Debug, Clone, PartialEq)]
11pub enum JsonValue {
12 Null,
14 Bool(bool),
16 Number(f64),
18 Str(String),
20 Array(Vec<JsonValue>),
22 Object(Vec<(String, JsonValue)>),
24}
25
26impl JsonValue {
27 pub fn to_json_string(&self) -> String {
29 match self {
30 JsonValue::Null => "null".to_string(),
31 JsonValue::Bool(b) => b.to_string(),
32 JsonValue::Number(n) => {
33 if n.is_nan() {
34 "null".to_string()
35 } else if n.is_infinite() {
36 if *n > 0.0 {
37 "1e308".to_string()
38 } else {
39 "-1e308".to_string()
40 }
41 } else if n.fract() == 0.0 && n.abs() < 1e15 {
42 format!("{}", *n as i64)
43 } else {
44 format!("{}", n)
45 }
46 }
47 JsonValue::Str(s) => {
48 let escaped = s
49 .replace('\\', "\\\\")
50 .replace('"', "\\\"")
51 .replace('\n', "\\n")
52 .replace('\r', "\\r")
53 .replace('\t', "\\t");
54 format!("\"{}\"", escaped)
55 }
56 JsonValue::Array(arr) => {
57 let parts: Vec<String> = arr.iter().map(|v| v.to_json_string()).collect();
58 format!("[{}]", parts.join(","))
59 }
60 JsonValue::Object(pairs) => {
61 let parts: Vec<String> = pairs
62 .iter()
63 .map(|(k, v)| format!("\"{}\":{}", k, v.to_json_string()))
64 .collect();
65 format!("{{{}}}", parts.join(","))
66 }
67 }
68 }
69
70 pub fn to_json_pretty(&self, indent: usize) -> String {
72 self.to_json_pretty_inner(indent, 0)
73 }
74
75 fn to_json_pretty_inner(&self, indent: usize, depth: usize) -> String {
76 let pad = " ".repeat(indent * depth);
77 let pad_inner = " ".repeat(indent * (depth + 1));
78 match self {
79 JsonValue::Null | JsonValue::Bool(_) | JsonValue::Number(_) | JsonValue::Str(_) => {
80 self.to_json_string()
81 }
82 JsonValue::Array(arr) => {
83 if arr.is_empty() {
84 return "[]".to_string();
85 }
86 let parts: Vec<String> = arr
87 .iter()
88 .map(|v| format!("{}{}", pad_inner, v.to_json_pretty_inner(indent, depth + 1)))
89 .collect();
90 format!("[\n{}\n{}]", parts.join(",\n"), pad)
91 }
92 JsonValue::Object(pairs) => {
93 if pairs.is_empty() {
94 return "{}".to_string();
95 }
96 let parts: Vec<String> = pairs
97 .iter()
98 .map(|(k, v)| {
99 format!(
100 "{}\"{}\": {}",
101 pad_inner,
102 k,
103 v.to_json_pretty_inner(indent, depth + 1)
104 )
105 })
106 .collect();
107 format!("{{\n{}\n{}}}", parts.join(",\n"), pad)
108 }
109 }
110 }
111
112 pub fn from_str(s: &str) -> Result<Self, String> {
114 let s = s.trim();
115 let (val, rest) = parse_value(s)?;
116 if rest.trim().is_empty() {
117 Ok(val)
118 } else {
119 Err(format!("Unexpected trailing input: {:?}", rest.trim()))
120 }
121 }
122
123 pub fn as_f64(&self) -> Option<f64> {
125 if let JsonValue::Number(n) = self {
126 Some(*n)
127 } else {
128 None
129 }
130 }
131
132 pub fn as_bool(&self) -> Option<bool> {
134 if let JsonValue::Bool(b) = self {
135 Some(*b)
136 } else {
137 None
138 }
139 }
140
141 pub fn as_str(&self) -> Option<&str> {
143 if let JsonValue::Str(s) = self {
144 Some(s.as_str())
145 } else {
146 None
147 }
148 }
149
150 pub fn as_array(&self) -> Option<&Vec<JsonValue>> {
152 if let JsonValue::Array(arr) = self {
153 Some(arr)
154 } else {
155 None
156 }
157 }
158
159 pub fn as_object(&self) -> Option<&Vec<(String, JsonValue)>> {
161 if let JsonValue::Object(pairs) = self {
162 Some(pairs)
163 } else {
164 None
165 }
166 }
167
168 pub fn get(&self, key: &str) -> Option<&JsonValue> {
170 if let JsonValue::Object(pairs) = self {
171 pairs.iter().find(|(k, _)| k == key).map(|(_, v)| v)
172 } else {
173 None
174 }
175 }
176
177 pub fn get_index(&self, i: usize) -> Option<&JsonValue> {
179 if let JsonValue::Array(arr) = self {
180 arr.get(i)
181 } else {
182 None
183 }
184 }
185
186 pub fn get_path(&self, path: &str) -> Option<&JsonValue> {
188 let mut current = self;
189 for key in path.split('.') {
190 current = current.get(key)?;
191 }
192 Some(current)
193 }
194
195 pub fn is_null(&self) -> bool {
197 matches!(self, JsonValue::Null)
198 }
199
200 pub fn len(&self) -> usize {
202 match self {
203 JsonValue::Array(arr) => arr.len(),
204 JsonValue::Object(pairs) => pairs.len(),
205 _ => 0,
206 }
207 }
208
209 pub fn is_empty(&self) -> bool {
211 match self {
212 JsonValue::Null => true,
213 JsonValue::Array(arr) => arr.is_empty(),
214 JsonValue::Object(pairs) => pairs.is_empty(),
215 _ => false,
216 }
217 }
218
219 pub fn merge(&self, other: &JsonValue) -> JsonValue {
222 match (self, other) {
223 (JsonValue::Object(base), JsonValue::Object(overlay)) => {
224 let mut merged = base.clone();
225 for (key, val) in overlay {
226 if let Some(pos) = merged.iter().position(|(k, _)| k == key) {
227 merged[pos] = (key.clone(), merged[pos].1.merge(val));
228 } else {
229 merged.push((key.clone(), val.clone()));
230 }
231 }
232 JsonValue::Object(merged)
233 }
234 _ => other.clone(),
235 }
236 }
237
238 pub fn keys(&self) -> Vec<&str> {
240 if let JsonValue::Object(pairs) = self {
241 pairs.iter().map(|(k, _)| k.as_str()).collect()
242 } else {
243 Vec::new()
244 }
245 }
246
247 pub fn values(&self) -> Vec<&JsonValue> {
249 if let JsonValue::Object(pairs) = self {
250 pairs.iter().map(|(_, v)| v).collect()
251 } else {
252 Vec::new()
253 }
254 }
255}
256
257#[derive(Debug, Clone)]
261pub enum JsonSchema {
262 Any,
264 Null,
266 Bool,
268 Number,
270 StringType,
272 ArrayOf(Box<JsonSchema>),
274 ObjectWith {
276 required: Vec<(String, JsonSchema)>,
278 allow_extra: bool,
280 },
281}
282
283impl JsonSchema {
284 pub fn validate(&self, value: &JsonValue) -> Result<(), String> {
286 match self {
287 JsonSchema::Any => Ok(()),
288 JsonSchema::Null => {
289 if value.is_null() {
290 Ok(())
291 } else {
292 Err("expected null".to_string())
293 }
294 }
295 JsonSchema::Bool => {
296 if value.as_bool().is_some() {
297 Ok(())
298 } else {
299 Err("expected boolean".to_string())
300 }
301 }
302 JsonSchema::Number => {
303 if value.as_f64().is_some() {
304 Ok(())
305 } else {
306 Err("expected number".to_string())
307 }
308 }
309 JsonSchema::StringType => {
310 if value.as_str().is_some() {
311 Ok(())
312 } else {
313 Err("expected string".to_string())
314 }
315 }
316 JsonSchema::ArrayOf(inner) => {
317 let arr = value.as_array().ok_or("expected array")?;
318 for (i, elem) in arr.iter().enumerate() {
319 inner
320 .validate(elem)
321 .map_err(|e| format!("array[{i}]: {e}"))?;
322 }
323 Ok(())
324 }
325 JsonSchema::ObjectWith {
326 required,
327 allow_extra,
328 } => {
329 let pairs = value.as_object().ok_or("expected object")?;
330 for (key, schema) in required {
331 let val = value
332 .get(key)
333 .ok_or_else(|| format!("missing required key '{key}'"))?;
334 schema
335 .validate(val)
336 .map_err(|e| format!("key '{key}': {e}"))?;
337 }
338 if !allow_extra {
339 let allowed: std::collections::HashSet<&str> =
340 required.iter().map(|(k, _)| k.as_str()).collect();
341 for (key, _) in pairs {
342 if !allowed.contains(key.as_str()) {
343 return Err(format!("unexpected key '{key}'"));
344 }
345 }
346 }
347 Ok(())
348 }
349 }
350 }
351}
352
353#[derive(Debug, Clone, PartialEq)]
357pub struct JsonDiff {
358 pub path: String,
360 pub left: Option<JsonValue>,
362 pub right: Option<JsonValue>,
364}
365
366pub fn json_diff(left: &JsonValue, right: &JsonValue) -> Vec<JsonDiff> {
368 let mut diffs = Vec::new();
369 json_diff_inner(left, right, String::new(), &mut diffs);
370 diffs
371}
372
373fn json_diff_inner(left: &JsonValue, right: &JsonValue, path: String, diffs: &mut Vec<JsonDiff>) {
374 if left == right {
375 return;
376 }
377 match (left, right) {
378 (JsonValue::Object(lp), JsonValue::Object(rp)) => {
379 let mut seen = std::collections::HashSet::new();
380 for (k, lv) in lp {
381 seen.insert(k.clone());
382 let child_path = if path.is_empty() {
383 k.clone()
384 } else {
385 format!("{}.{}", path, k)
386 };
387 if let Some(rv) = rp.iter().find(|(rk, _)| rk == k).map(|(_, v)| v) {
388 json_diff_inner(lv, rv, child_path, diffs);
389 } else {
390 diffs.push(JsonDiff {
391 path: child_path,
392 left: Some(lv.clone()),
393 right: None,
394 });
395 }
396 }
397 for (k, rv) in rp {
398 if !seen.contains(k) {
399 let child_path = if path.is_empty() {
400 k.clone()
401 } else {
402 format!("{}.{}", path, k)
403 };
404 diffs.push(JsonDiff {
405 path: child_path,
406 left: None,
407 right: Some(rv.clone()),
408 });
409 }
410 }
411 }
412 (JsonValue::Array(la), JsonValue::Array(ra)) => {
413 let max_len = la.len().max(ra.len());
414 for i in 0..max_len {
415 let child_path = if path.is_empty() {
416 format!("[{i}]")
417 } else {
418 format!("{path}[{i}]")
419 };
420 match (la.get(i), ra.get(i)) {
421 (Some(lv), Some(rv)) => json_diff_inner(lv, rv, child_path, diffs),
422 (Some(lv), None) => diffs.push(JsonDiff {
423 path: child_path,
424 left: Some(lv.clone()),
425 right: None,
426 }),
427 (None, Some(rv)) => diffs.push(JsonDiff {
428 path: child_path,
429 left: None,
430 right: Some(rv.clone()),
431 }),
432 (None, None) => {}
433 }
434 }
435 }
436 _ => {
437 diffs.push(JsonDiff {
438 path: if path.is_empty() {
439 "$".to_string()
440 } else {
441 path
442 },
443 left: Some(left.clone()),
444 right: Some(right.clone()),
445 });
446 }
447 }
448}
449
450pub struct JsonStreamParser {
456 buffer: String,
457 results: Vec<JsonValue>,
458}
459
460impl JsonStreamParser {
461 pub fn new() -> Self {
463 Self {
464 buffer: String::new(),
465 results: Vec::new(),
466 }
467 }
468
469 pub fn feed(&mut self, chunk: &str) -> usize {
472 self.buffer.push_str(chunk);
473 let mut count = 0;
474 loop {
475 let trimmed = self.buffer.trim_start();
476 if trimmed.is_empty() {
477 break;
478 }
479 let first = trimmed.as_bytes()[0];
481 if first == b'[' || first == b',' {
482 self.buffer = trimmed[1..].to_string();
483 continue;
484 }
485 if first == b']' {
486 self.buffer = trimmed[1..].to_string();
487 continue;
488 }
489 match parse_value(trimmed) {
490 Ok((val, rest)) => {
491 self.results.push(val);
492 self.buffer = rest.to_string();
493 count += 1;
494 }
495 Err(_) => break, }
497 }
498 count
499 }
500
501 pub fn take_results(&mut self) -> Vec<JsonValue> {
503 std::mem::take(&mut self.results)
504 }
505
506 pub fn result_count(&self) -> usize {
508 self.results.len()
509 }
510}
511impl Default for JsonStreamParser {
512 fn default() -> Self {
513 Self::new()
514 }
515}
516
517pub fn records_to_json(records: &[Vec<(String, JsonValue)>]) -> JsonValue {
521 let arr: Vec<JsonValue> = records
522 .iter()
523 .map(|rec| JsonValue::Object(rec.clone()))
524 .collect();
525 JsonValue::Array(arr)
526}
527
528pub fn json_to_records(value: &JsonValue) -> Option<Vec<Vec<(String, JsonValue)>>> {
530 let arr = value.as_array()?;
531 let mut records = Vec::new();
532 for item in arr {
533 let pairs = item.as_object()?;
534 records.push(pairs.clone());
535 }
536 Some(records)
537}
538
539fn skip_whitespace(s: &str) -> &str {
542 s.trim_start_matches(|c: char| c.is_ascii_whitespace())
543}
544
545fn parse_value(s: &str) -> Result<(JsonValue, &str), String> {
546 let s = skip_whitespace(s);
547 if s.is_empty() {
548 return Err("Unexpected end of input".to_string());
549 }
550 match s.as_bytes()[0] {
551 b'n' => parse_null(s),
552 b't' | b'f' => parse_bool(s),
553 b'"' => parse_string(s),
554 b'[' => parse_array(s),
555 b'{' => parse_object(s),
556 b'-' | b'0'..=b'9' => parse_number(s),
557 c => Err(format!("Unexpected character: '{}'", c as char)),
558 }
559}
560
561fn parse_null(s: &str) -> Result<(JsonValue, &str), String> {
562 s.strip_prefix("null")
563 .map(|rest| (JsonValue::Null, rest))
564 .ok_or_else(|| format!("Expected 'null', got: {:?}", &s[..s.len().min(6)]))
565}
566
567fn parse_bool(s: &str) -> Result<(JsonValue, &str), String> {
568 if let Some(rest) = s.strip_prefix("true") {
569 return Ok((JsonValue::Bool(true), rest));
570 }
571 if let Some(rest) = s.strip_prefix("false") {
572 return Ok((JsonValue::Bool(false), rest));
573 }
574 Err(format!("Expected boolean, got: {:?}", &s[..s.len().min(6)]))
575}
576
577fn parse_number(s: &str) -> Result<(JsonValue, &str), String> {
578 let end = s
579 .find(|c: char| !matches!(c, '-' | '+' | '.' | 'e' | 'E' | '0'..='9'))
580 .unwrap_or(s.len());
581 let num_str = &s[..end];
582 let n: f64 = num_str
583 .parse()
584 .map_err(|e| format!("Number parse error '{}': {}", num_str, e))?;
585 Ok((JsonValue::Number(n), &s[end..]))
586}
587
588fn parse_string(s: &str) -> Result<(JsonValue, &str), String> {
589 let s = &s[1..]; let mut result = String::new();
592 let mut chars = s.char_indices();
593 loop {
594 match chars.next() {
595 None => return Err("Unterminated string".to_string()),
596 Some((_, '"')) => {
597 let pos = s.len() - chars.as_str().len();
599 return Ok((JsonValue::Str(result), &s[pos..]));
600 }
601 Some((_, '\\')) => match chars.next() {
602 Some((_, '"')) => result.push('"'),
603 Some((_, '\\')) => result.push('\\'),
604 Some((_, '/')) => result.push('/'),
605 Some((_, 'n')) => result.push('\n'),
606 Some((_, 'r')) => result.push('\r'),
607 Some((_, 't')) => result.push('\t'),
608 Some((_, 'b')) => result.push('\x08'),
609 Some((_, 'f')) => result.push('\x0C'),
610 Some((_, 'u')) => {
611 let hex: String = (0..4)
613 .map(|_| chars.next().map(|(_, c)| c).unwrap_or('0'))
614 .collect();
615 let code = u32::from_str_radix(&hex, 16)
616 .map_err(|_| format!("Invalid unicode escape \\u{}", hex))?;
617 let ch = char::from_u32(code)
618 .ok_or_else(|| format!("Invalid unicode codepoint U+{:04X}", code))?;
619 result.push(ch);
620 }
621 Some((_, c)) => return Err(format!("Invalid escape \\{}", c)),
622 None => return Err("Unterminated escape".to_string()),
623 },
624 Some((_, c)) => result.push(c),
625 }
626 }
627}
628
629fn parse_array(s: &str) -> Result<(JsonValue, &str), String> {
630 let mut s = skip_whitespace(&s[1..]);
632 let mut items = Vec::new();
633 if s.starts_with(']') {
634 return Ok((JsonValue::Array(items), &s[1..]));
635 }
636 loop {
637 let (val, rest) = parse_value(s)?;
638 items.push(val);
639 s = skip_whitespace(rest);
640 if s.starts_with(']') {
641 return Ok((JsonValue::Array(items), &s[1..]));
642 }
643 if s.starts_with(',') {
644 s = skip_whitespace(&s[1..]);
645 } else {
646 return Err(format!(
647 "Expected ',' or ']' in array, got: {:?}",
648 &s[..s.len().min(5)]
649 ));
650 }
651 }
652}
653
654fn parse_object(s: &str) -> Result<(JsonValue, &str), String> {
655 let mut s = skip_whitespace(&s[1..]);
657 let mut pairs = Vec::new();
658 if s.starts_with('}') {
659 return Ok((JsonValue::Object(pairs), &s[1..]));
660 }
661 loop {
662 s = skip_whitespace(s);
663 if !s.starts_with('"') {
665 return Err(format!(
666 "Expected string key in object, got: {:?}",
667 &s[..s.len().min(5)]
668 ));
669 }
670 let (key_val, rest) = parse_string(s)?;
671 let key = match key_val {
672 JsonValue::Str(k) => k,
673 _ => unreachable!(),
674 };
675 s = skip_whitespace(rest);
676 if !s.starts_with(':') {
677 return Err(format!(
678 "Expected ':' after key, got: {:?}",
679 &s[..s.len().min(5)]
680 ));
681 }
682 s = skip_whitespace(&s[1..]);
683 let (val, rest) = parse_value(s)?;
684 pairs.push((key, val));
685 s = skip_whitespace(rest);
686 if s.starts_with('}') {
687 return Ok((JsonValue::Object(pairs), &s[1..]));
688 }
689 if s.starts_with(',') {
690 s = skip_whitespace(&s[1..]);
691 } else {
692 return Err(format!(
693 "Expected ',' or '}}' in object, got: {:?}",
694 &s[..s.len().min(5)]
695 ));
696 }
697 }
698}
699
700pub fn vec3_to_json(v: [f64; 3]) -> JsonValue {
704 JsonValue::Array(v.iter().copied().map(JsonValue::Number).collect())
705}
706
707pub fn json_to_vec3(j: &JsonValue) -> Option<[f64; 3]> {
709 let arr = j.as_array()?;
710 if arr.len() < 3 {
711 return None;
712 }
713 Some([arr[0].as_f64()?, arr[1].as_f64()?, arr[2].as_f64()?])
714}
715
716pub fn matrix_to_json(rows: &[Vec<f64>]) -> JsonValue {
718 JsonValue::Array(
719 rows.iter()
720 .map(|row| JsonValue::Array(row.iter().copied().map(JsonValue::Number).collect()))
721 .collect(),
722 )
723}
724
725pub fn particles_to_json(positions: &[[f64; 3]], velocities: &[[f64; 3]]) -> JsonValue {
728 let particles: Vec<JsonValue> = positions
729 .iter()
730 .zip(velocities.iter())
731 .map(|(pos, vel)| {
732 JsonValue::Object(vec![
733 ("position".to_string(), vec3_to_json(*pos)),
734 ("velocity".to_string(), vec3_to_json(*vel)),
735 ])
736 })
737 .collect();
738 JsonValue::Object(vec![("particles".to_string(), JsonValue::Array(particles))])
739}
740
741pub struct JsonStreamingWriter {
749 buffer: String,
750 stack: Vec<(bool, bool)>,
752}
753
754impl JsonStreamingWriter {
755 pub fn new() -> Self {
757 Self {
758 buffer: String::new(),
759 stack: Vec::new(),
760 }
761 }
762
763 pub fn begin_array(&mut self) {
765 self.maybe_comma();
766 self.buffer.push('[');
767 self.stack.push((true, false));
768 }
769
770 pub fn end_array(&mut self) {
772 self.buffer.push(']');
773 self.stack.pop();
774 if let Some(top) = self.stack.last_mut() {
775 top.1 = true; }
777 }
778
779 pub fn begin_object(&mut self) {
781 self.maybe_comma();
782 self.buffer.push('{');
783 self.stack.push((false, false));
784 }
785
786 pub fn end_object(&mut self) {
788 self.buffer.push('}');
789 self.stack.pop();
790 if let Some(top) = self.stack.last_mut() {
791 top.1 = true;
792 }
793 }
794
795 pub fn write_key(&mut self, key: &str) {
797 self.maybe_comma();
798 let escaped = key.replace('\\', "\\\\").replace('"', "\\\"");
799 self.buffer.push_str(&format!("\"{}\":", escaped));
800 if let Some(top) = self.stack.last_mut() {
802 top.1 = false;
803 }
804 }
805
806 pub fn write_value(&mut self, value: &JsonValue) {
808 self.maybe_comma();
809 self.buffer.push_str(&value.to_json_string());
810 if let Some(top) = self.stack.last_mut() {
811 top.1 = true;
812 }
813 }
814
815 pub fn finish(self) -> String {
817 self.buffer
818 }
819
820 fn maybe_comma(&mut self) {
821 if let Some(&(_, needs_comma)) = self.stack.last()
822 && needs_comma
823 {
824 self.buffer.push(',');
825 }
826 }
827}
828
829impl Default for JsonStreamingWriter {
830 fn default() -> Self {
831 Self::new()
832 }
833}
834
835impl JsonSchema {
838 pub fn infer_from(value: &JsonValue) -> Self {
843 match value {
844 JsonValue::Null => JsonSchema::Null,
845 JsonValue::Bool(_) => JsonSchema::Bool,
846 JsonValue::Number(_) => JsonSchema::Number,
847 JsonValue::Str(_) => JsonSchema::StringType,
848 JsonValue::Array(arr) => {
849 let inner = arr.first().map(Self::infer_from).unwrap_or(JsonSchema::Any);
850 JsonSchema::ArrayOf(Box::new(inner))
851 }
852 JsonValue::Object(pairs) => {
853 let required = pairs
854 .iter()
855 .map(|(k, v)| (k.clone(), Self::infer_from(v)))
856 .collect();
857 JsonSchema::ObjectWith {
858 required,
859 allow_extra: true,
860 }
861 }
862 }
863 }
864
865 pub fn to_schema_string(&self) -> String {
867 match self {
868 JsonSchema::Any => "any".to_string(),
869 JsonSchema::Null => "null".to_string(),
870 JsonSchema::Bool => "boolean".to_string(),
871 JsonSchema::Number => "number".to_string(),
872 JsonSchema::StringType => "string".to_string(),
873 JsonSchema::ArrayOf(inner) => format!("array<{}>", inner.to_schema_string()),
874 JsonSchema::ObjectWith {
875 required,
876 allow_extra,
877 } => {
878 let fields: Vec<String> = required
879 .iter()
880 .map(|(k, s)| format!("{}: {}", k, s.to_schema_string()))
881 .collect();
882 let extra = if *allow_extra { ", ..." } else { "" };
883 format!("object{{{}{}}}", fields.join(", "), extra)
884 }
885 }
886 }
887
888 pub fn to_json_schema_object(&self) -> JsonValue {
890 match self {
891 JsonSchema::Any => JsonValue::Object(vec![]),
892 JsonSchema::Null => {
893 JsonValue::Object(vec![("type".into(), JsonValue::Str("null".into()))])
894 }
895 JsonSchema::Bool => {
896 JsonValue::Object(vec![("type".into(), JsonValue::Str("boolean".into()))])
897 }
898 JsonSchema::Number => {
899 JsonValue::Object(vec![("type".into(), JsonValue::Str("number".into()))])
900 }
901 JsonSchema::StringType => {
902 JsonValue::Object(vec![("type".into(), JsonValue::Str("string".into()))])
903 }
904 JsonSchema::ArrayOf(inner) => JsonValue::Object(vec![
905 ("type".into(), JsonValue::Str("array".into())),
906 ("items".into(), inner.to_json_schema_object()),
907 ]),
908 JsonSchema::ObjectWith {
909 required,
910 allow_extra,
911 } => {
912 let props: Vec<(String, JsonValue)> = required
913 .iter()
914 .map(|(k, s)| (k.clone(), s.to_json_schema_object()))
915 .collect();
916 let req_names: Vec<JsonValue> = required
917 .iter()
918 .map(|(k, _)| JsonValue::Str(k.clone()))
919 .collect();
920 JsonValue::Object(vec![
921 ("type".into(), JsonValue::Str("object".into())),
922 ("properties".into(), JsonValue::Object(props)),
923 ("required".into(), JsonValue::Array(req_names)),
924 ("additionalProperties".into(), JsonValue::Bool(*allow_extra)),
925 ])
926 }
927 }
928 }
929}
930
931#[derive(Debug, Clone)]
935pub enum JsonPatch {
936 Add {
938 path: String,
940 value: JsonValue,
942 },
943 Remove {
945 path: String,
947 },
948 Replace {
950 path: String,
952 value: JsonValue,
954 },
955 Copy {
957 from: String,
959 to: String,
961 },
962}
963
964impl JsonPatch {
965 pub fn apply(&self, doc: &mut JsonValue) -> Result<(), String> {
967 match self {
968 JsonPatch::Add { path, value } => patch_set_key(doc, path, value.clone()),
969 JsonPatch::Remove { path } => patch_remove_key(doc, path),
970 JsonPatch::Replace { path, value } => patch_set_key(doc, path, value.clone()),
971 JsonPatch::Copy { from, to } => {
972 let val = patch_get_key(doc, from)
973 .ok_or_else(|| format!("path '{}' not found for copy", from))?
974 .clone();
975 patch_set_key(doc, to, val)
976 }
977 }
978 }
979
980 pub fn to_json(&self) -> JsonValue {
982 match self {
983 JsonPatch::Add { path, value } => JsonValue::Object(vec![
984 ("op".into(), JsonValue::Str("add".into())),
985 ("path".into(), JsonValue::Str(path.clone())),
986 ("value".into(), value.clone()),
987 ]),
988 JsonPatch::Remove { path } => JsonValue::Object(vec![
989 ("op".into(), JsonValue::Str("remove".into())),
990 ("path".into(), JsonValue::Str(path.clone())),
991 ]),
992 JsonPatch::Replace { path, value } => JsonValue::Object(vec![
993 ("op".into(), JsonValue::Str("replace".into())),
994 ("path".into(), JsonValue::Str(path.clone())),
995 ("value".into(), value.clone()),
996 ]),
997 JsonPatch::Copy { from, to } => JsonValue::Object(vec![
998 ("op".into(), JsonValue::Str("copy".into())),
999 ("from".into(), JsonValue::Str(from.clone())),
1000 ("path".into(), JsonValue::Str(to.clone())),
1001 ]),
1002 }
1003 }
1004}
1005
1006fn patch_set_key(doc: &mut JsonValue, key: &str, value: JsonValue) -> Result<(), String> {
1007 if let JsonValue::Object(pairs) = doc {
1008 if let Some(pos) = pairs.iter().position(|(k, _)| k == key) {
1009 pairs[pos].1 = value;
1010 } else {
1011 pairs.push((key.to_string(), value));
1012 }
1013 Ok(())
1014 } else {
1015 Err(format!("cannot set key '{}' on non-object", key))
1016 }
1017}
1018
1019fn patch_remove_key(doc: &mut JsonValue, key: &str) -> Result<(), String> {
1020 if let JsonValue::Object(pairs) = doc {
1021 let before = pairs.len();
1022 pairs.retain(|(k, _)| k != key);
1023 if pairs.len() < before {
1024 Ok(())
1025 } else {
1026 Err(format!("key '{}' not found for remove", key))
1027 }
1028 } else {
1029 Err(format!("cannot remove key '{}' from non-object", key))
1030 }
1031}
1032
1033fn patch_get_key<'a>(doc: &'a JsonValue, key: &str) -> Option<&'a JsonValue> {
1034 doc.get(key)
1035}
1036
1037pub fn apply_patch_sequence(doc: &mut JsonValue, patches: &[JsonPatch]) -> Result<(), String> {
1039 for patch in patches {
1040 patch.apply(doc)?;
1041 }
1042 Ok(())
1043}
1044
1045pub fn generate_patch(left: &JsonValue, right: &JsonValue) -> Vec<JsonPatch> {
1047 let diffs = json_diff(left, right);
1048 diffs
1049 .into_iter()
1050 .map(|diff| match (diff.left, diff.right) {
1051 (None, Some(v)) => JsonPatch::Add {
1052 path: diff.path,
1053 value: v,
1054 },
1055 (Some(_), None) => JsonPatch::Remove { path: diff.path },
1056 (Some(_), Some(v)) => JsonPatch::Replace {
1057 path: diff.path,
1058 value: v,
1059 },
1060 (None, None) => JsonPatch::Remove { path: diff.path },
1061 })
1062 .collect()
1063}
1064
1065pub fn jsonpath_query<'a>(root: &'a JsonValue, path: &str) -> Vec<&'a JsonValue> {
1076 let path = path.trim();
1077 if path == "$" {
1078 return vec![root];
1079 }
1080 if !path.starts_with('$') {
1081 return vec![];
1082 }
1083
1084 let rest = &path[1..]; let segments = parse_jsonpath_segments(rest);
1087 let mut current: Vec<&'a JsonValue> = vec![root];
1088
1089 for seg in segments {
1090 let mut next = Vec::new();
1091 for node in current {
1092 match seg.as_str() {
1093 "*" => match node {
1094 JsonValue::Object(pairs) => {
1095 for (_, v) in pairs {
1096 next.push(v);
1097 }
1098 }
1099 JsonValue::Array(arr) => {
1100 for v in arr {
1101 next.push(v);
1102 }
1103 }
1104 _ => {}
1105 },
1106 s if s.starts_with('[') && s.ends_with(']') => {
1107 let inner = &s[1..s.len() - 1];
1108 if inner == "*" {
1109 if let JsonValue::Array(arr) = node {
1110 for v in arr {
1111 next.push(v);
1112 }
1113 }
1114 } else if let Ok(idx) = inner.parse::<usize>()
1115 && let JsonValue::Array(arr) = node
1116 && let Some(v) = arr.get(idx)
1117 {
1118 next.push(v);
1119 }
1120 }
1121 key => {
1122 if let Some(v) = node.get(key) {
1123 next.push(v);
1124 }
1125 }
1126 }
1127 }
1128 current = next;
1129 }
1130 current
1131}
1132
1133fn parse_jsonpath_segments(s: &str) -> Vec<String> {
1134 let mut segments = Vec::new();
1135 let mut remaining = s;
1136
1137 while !remaining.is_empty() {
1138 if remaining.starts_with('.') {
1139 remaining = &remaining[1..];
1140 let end = remaining.find(['.', '[']).unwrap_or(remaining.len());
1142 if end > 0 {
1143 segments.push(remaining[..end].to_string());
1144 remaining = &remaining[end..];
1145 }
1146 } else if remaining.starts_with('[') {
1147 let end = remaining.find(']').unwrap_or(remaining.len() - 1);
1148 segments.push(remaining[..=end].to_string());
1149 remaining = &remaining[(end + 1)..];
1150 } else {
1151 let end = remaining.find(['.', '[']).unwrap_or(remaining.len());
1153 segments.push(remaining[..end].to_string());
1154 remaining = &remaining[end..];
1155 }
1156 }
1157 segments
1158}
1159
1160pub fn json_compress(input: &str) -> String {
1166 let mut out = String::with_capacity(input.len());
1167 let mut in_string = false;
1168 let mut chars = input.chars().peekable();
1169
1170 while let Some(c) = chars.next() {
1171 if in_string {
1172 out.push(c);
1173 if c == '\\' {
1174 if let Some(next) = chars.next() {
1176 out.push(next);
1177 }
1178 } else if c == '"' {
1179 in_string = false;
1180 }
1181 } else {
1182 match c {
1183 '"' => {
1184 in_string = true;
1185 out.push(c);
1186 }
1187 ' ' | '\t' | '\n' | '\r' => {
1188 }
1190 _ => {
1191 out.push(c);
1192 }
1193 }
1194 }
1195 }
1196 out
1197}
1198
1199#[derive(Debug, Clone)]
1201pub struct JsonCompressionStats {
1202 pub original_len: usize,
1204 pub compressed_len: usize,
1206}
1207
1208impl JsonCompressionStats {
1209 pub fn compute(input: &str) -> Self {
1211 let compressed = json_compress(input);
1212 Self {
1213 original_len: input.len(),
1214 compressed_len: compressed.len(),
1215 }
1216 }
1217
1218 pub fn compression_ratio(&self) -> f64 {
1220 if self.original_len == 0 {
1221 return 1.0;
1222 }
1223 self.compressed_len as f64 / self.original_len as f64
1224 }
1225
1226 pub fn bytes_saved(&self) -> usize {
1228 self.original_len.saturating_sub(self.compressed_len)
1229 }
1230}
1231
1232#[cfg(test)]
1235mod tests {
1236 use super::*;
1237
1238 #[test]
1239 fn test_null_roundtrip() {
1240 let j = JsonValue::Null;
1241 let s = j.to_json_string();
1242 assert_eq!(s, "null");
1243 let parsed = JsonValue::from_str(&s).unwrap();
1244 assert_eq!(parsed, JsonValue::Null);
1245 }
1246
1247 #[test]
1248 fn test_bool_true() {
1249 let j = JsonValue::Bool(true);
1250 let s = j.to_json_string();
1251 assert_eq!(s, "true");
1252 assert_eq!(JsonValue::from_str(&s).unwrap(), JsonValue::Bool(true));
1253 }
1254
1255 #[test]
1256 fn test_bool_false() {
1257 let j = JsonValue::Bool(false);
1258 assert_eq!(j.to_json_string(), "false");
1259 assert_eq!(
1260 JsonValue::from_str("false").unwrap(),
1261 JsonValue::Bool(false)
1262 );
1263 }
1264
1265 #[test]
1266 fn test_number_integer() {
1267 let j = JsonValue::Number(42.0);
1268 let s = j.to_json_string();
1269 assert_eq!(s, "42");
1270 let parsed = JsonValue::from_str(&s).unwrap();
1271 assert_eq!(parsed.as_f64(), Some(42.0));
1272 }
1273
1274 #[test]
1275 fn test_number_float() {
1276 let j = JsonValue::Number(3.125);
1277 let s = j.to_json_string();
1278 let parsed = JsonValue::from_str(&s).unwrap();
1279 let v = parsed.as_f64().unwrap();
1280 assert!((v - 3.125).abs() < 1e-10);
1281 }
1282
1283 #[test]
1284 fn test_string_roundtrip() {
1285 let j = JsonValue::Str("hello world".to_string());
1286 let s = j.to_json_string();
1287 assert_eq!(s, "\"hello world\"");
1288 let parsed = JsonValue::from_str(&s).unwrap();
1289 assert_eq!(parsed.as_str(), Some("hello world"));
1290 }
1291
1292 #[test]
1293 fn test_string_escape() {
1294 let j = JsonValue::Str("line1\nline2".to_string());
1295 let s = j.to_json_string();
1296 assert!(s.contains("\\n"));
1297 let parsed = JsonValue::from_str(&s).unwrap();
1298 assert_eq!(parsed.as_str(), Some("line1\nline2"));
1299 }
1300
1301 #[test]
1302 fn test_array_roundtrip() {
1303 let j = JsonValue::Array(vec![
1304 JsonValue::Number(1.0),
1305 JsonValue::Number(2.0),
1306 JsonValue::Number(3.0),
1307 ]);
1308 let s = j.to_json_string();
1309 let parsed = JsonValue::from_str(&s).unwrap();
1310 let arr = parsed.as_array().unwrap();
1311 assert_eq!(arr.len(), 3);
1312 assert_eq!(arr[0].as_f64(), Some(1.0));
1313 }
1314
1315 #[test]
1316 fn test_empty_array() {
1317 let j = JsonValue::Array(vec![]);
1318 assert_eq!(j.to_json_string(), "[]");
1319 let parsed = JsonValue::from_str("[]").unwrap();
1320 assert_eq!(parsed.as_array().unwrap().len(), 0);
1321 }
1322
1323 #[test]
1324 fn test_object_get() {
1325 let j = JsonValue::Object(vec![
1326 ("x".to_string(), JsonValue::Number(1.0)),
1327 ("y".to_string(), JsonValue::Number(2.0)),
1328 ]);
1329 assert_eq!(j.get("x").unwrap().as_f64(), Some(1.0));
1330 assert!(j.get("z").is_none());
1331 }
1332
1333 #[test]
1334 fn test_object_roundtrip() {
1335 let j = JsonValue::Object(vec![
1336 ("name".to_string(), JsonValue::Str("particle".to_string())),
1337 ("mass".to_string(), JsonValue::Number(1.5)),
1338 ]);
1339 let s = j.to_json_string();
1340 let parsed = JsonValue::from_str(&s).unwrap();
1341 assert_eq!(parsed.get("name").unwrap().as_str(), Some("particle"));
1342 assert_eq!(parsed.get("mass").unwrap().as_f64(), Some(1.5));
1343 }
1344
1345 #[test]
1346 fn test_nested_object() {
1347 let s = r#"{"pos":{"x":1,"y":2}}"#;
1348 let parsed = JsonValue::from_str(s).unwrap();
1349 let pos = parsed.get("pos").unwrap();
1350 assert_eq!(pos.get("x").unwrap().as_f64(), Some(1.0));
1351 }
1352
1353 #[test]
1354 fn test_get_index() {
1355 let j = JsonValue::Array(vec![JsonValue::Number(10.0), JsonValue::Number(20.0)]);
1356 assert_eq!(j.get_index(0).unwrap().as_f64(), Some(10.0));
1357 assert!(j.get_index(5).is_none());
1358 }
1359
1360 #[test]
1361 fn test_vec3_roundtrip() {
1362 let v = [1.0, 2.0, 3.0f64];
1363 let j = vec3_to_json(v);
1364 let v2 = json_to_vec3(&j).unwrap();
1365 assert_eq!(v, v2);
1366 }
1367
1368 #[test]
1369 fn test_json_to_vec3_too_short() {
1370 let j = JsonValue::Array(vec![JsonValue::Number(1.0)]);
1371 assert!(json_to_vec3(&j).is_none());
1372 }
1373
1374 #[test]
1375 fn test_particles_to_json() {
1376 let pos = [[0.0, 0.0, 0.0], [1.0, 2.0, 3.0]];
1377 let vel = [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]];
1378 let j = particles_to_json(&pos, &vel);
1379 let arr = j.get("particles").unwrap().as_array().unwrap();
1380 assert_eq!(arr.len(), 2);
1381 let p0 = &arr[0];
1382 let p0_pos = json_to_vec3(p0.get("position").unwrap()).unwrap();
1383 assert_eq!(p0_pos, [0.0, 0.0, 0.0]);
1384 }
1385
1386 #[test]
1387 fn test_parse_negative_number() {
1388 let j = JsonValue::from_str("-3.125").unwrap();
1389 assert!((j.as_f64().unwrap() + 3.125).abs() < 1e-10);
1390 }
1391
1392 #[test]
1393 fn test_whitespace_tolerance() {
1394 let s = " { \"k\" : 42 } ";
1395 let j = JsonValue::from_str(s).unwrap();
1396 assert_eq!(j.get("k").unwrap().as_f64(), Some(42.0));
1397 }
1398
1399 #[test]
1400 fn test_empty_object() {
1401 let j = JsonValue::from_str("{}").unwrap();
1402 assert!(j.get("anything").is_none());
1403 }
1404
1405 #[test]
1406 fn test_invalid_input_error() {
1407 assert!(JsonValue::from_str("not_json").is_err());
1408 }
1409
1410 #[test]
1413 fn test_get_path() {
1414 let s = r#"{"a":{"b":{"c":42}}}"#;
1415 let j = JsonValue::from_str(s).unwrap();
1416 assert_eq!(j.get_path("a.b.c").unwrap().as_f64(), Some(42.0));
1417 assert!(j.get_path("a.b.d").is_none());
1418 assert!(j.get_path("x").is_none());
1419 }
1420
1421 #[test]
1422 fn test_json_len_and_is_empty() {
1423 let arr = JsonValue::Array(vec![JsonValue::Number(1.0), JsonValue::Number(2.0)]);
1424 assert_eq!(arr.len(), 2);
1425 assert!(!arr.is_empty());
1426
1427 let empty_arr = JsonValue::Array(vec![]);
1428 assert_eq!(empty_arr.len(), 0);
1429 assert!(empty_arr.is_empty());
1430
1431 assert!(JsonValue::Null.is_empty());
1432 assert!(!JsonValue::Number(1.0).is_empty());
1433 }
1434
1435 #[test]
1436 fn test_json_merge() {
1437 let base = JsonValue::from_str(r#"{"a":1,"b":2}"#).unwrap();
1438 let overlay = JsonValue::from_str(r#"{"b":3,"c":4}"#).unwrap();
1439 let merged = base.merge(&overlay);
1440 assert_eq!(merged.get("a").unwrap().as_f64(), Some(1.0));
1441 assert_eq!(merged.get("b").unwrap().as_f64(), Some(3.0));
1442 assert_eq!(merged.get("c").unwrap().as_f64(), Some(4.0));
1443 }
1444
1445 #[test]
1446 fn test_json_merge_nested() {
1447 let base = JsonValue::from_str(r#"{"a":{"x":1,"y":2}}"#).unwrap();
1448 let overlay = JsonValue::from_str(r#"{"a":{"y":3,"z":4}}"#).unwrap();
1449 let merged = base.merge(&overlay);
1450 let a = merged.get("a").unwrap();
1451 assert_eq!(a.get("x").unwrap().as_f64(), Some(1.0));
1452 assert_eq!(a.get("y").unwrap().as_f64(), Some(3.0));
1453 assert_eq!(a.get("z").unwrap().as_f64(), Some(4.0));
1454 }
1455
1456 #[test]
1457 fn test_json_keys_values() {
1458 let j = JsonValue::from_str(r#"{"a":1,"b":2}"#).unwrap();
1459 let keys = j.keys();
1460 assert_eq!(keys.len(), 2);
1461 assert!(keys.contains(&"a"));
1462 assert!(keys.contains(&"b"));
1463 let vals = j.values();
1464 assert_eq!(vals.len(), 2);
1465 }
1466
1467 #[test]
1468 fn test_schema_validation_number() {
1469 let schema = JsonSchema::Number;
1470 assert!(schema.validate(&JsonValue::Number(42.0)).is_ok());
1471 assert!(schema.validate(&JsonValue::Str("nope".into())).is_err());
1472 }
1473
1474 #[test]
1475 fn test_schema_validation_object() {
1476 let schema = JsonSchema::ObjectWith {
1477 required: vec![
1478 ("name".into(), JsonSchema::StringType),
1479 ("mass".into(), JsonSchema::Number),
1480 ],
1481 allow_extra: true,
1482 };
1483 let valid = JsonValue::from_str(r#"{"name":"ball","mass":1.5,"extra":true}"#).unwrap();
1484 assert!(schema.validate(&valid).is_ok());
1485
1486 let missing_mass = JsonValue::from_str(r#"{"name":"ball"}"#).unwrap();
1487 assert!(schema.validate(&missing_mass).is_err());
1488 }
1489
1490 #[test]
1491 fn test_schema_no_extra_keys() {
1492 let schema = JsonSchema::ObjectWith {
1493 required: vec![("x".into(), JsonSchema::Number)],
1494 allow_extra: false,
1495 };
1496 let valid = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1497 assert!(schema.validate(&valid).is_ok());
1498
1499 let extra = JsonValue::from_str(r#"{"x":1,"y":2}"#).unwrap();
1500 assert!(schema.validate(&extra).is_err());
1501 }
1502
1503 #[test]
1504 fn test_schema_array_of() {
1505 let schema = JsonSchema::ArrayOf(Box::new(JsonSchema::Number));
1506 let valid = JsonValue::from_str("[1,2,3]").unwrap();
1507 assert!(schema.validate(&valid).is_ok());
1508
1509 let invalid = JsonValue::from_str(r#"[1,"two",3]"#).unwrap();
1510 assert!(schema.validate(&invalid).is_err());
1511 }
1512
1513 #[test]
1514 fn test_json_diff_same() {
1515 let a = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1516 let b = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1517 let diffs = json_diff(&a, &b);
1518 assert!(diffs.is_empty());
1519 }
1520
1521 #[test]
1522 fn test_json_diff_different_value() {
1523 let a = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1524 let b = JsonValue::from_str(r#"{"x":2}"#).unwrap();
1525 let diffs = json_diff(&a, &b);
1526 assert_eq!(diffs.len(), 1);
1527 assert_eq!(diffs[0].path, "x");
1528 }
1529
1530 #[test]
1531 fn test_json_diff_added_key() {
1532 let a = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1533 let b = JsonValue::from_str(r#"{"x":1,"y":2}"#).unwrap();
1534 let diffs = json_diff(&a, &b);
1535 assert_eq!(diffs.len(), 1);
1536 assert_eq!(diffs[0].path, "y");
1537 assert!(diffs[0].left.is_none());
1538 assert!(diffs[0].right.is_some());
1539 }
1540
1541 #[test]
1542 fn test_json_diff_removed_key() {
1543 let a = JsonValue::from_str(r#"{"x":1,"y":2}"#).unwrap();
1544 let b = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1545 let diffs = json_diff(&a, &b);
1546 assert_eq!(diffs.len(), 1);
1547 assert_eq!(diffs[0].path, "y");
1548 assert!(diffs[0].left.is_some());
1549 assert!(diffs[0].right.is_none());
1550 }
1551
1552 #[test]
1553 fn test_json_diff_array() {
1554 let a = JsonValue::from_str("[1,2,3]").unwrap();
1555 let b = JsonValue::from_str("[1,2,4]").unwrap();
1556 let diffs = json_diff(&a, &b);
1557 assert_eq!(diffs.len(), 1);
1558 assert!(diffs[0].path.contains("[2]"));
1559 }
1560
1561 #[test]
1562 fn test_streaming_parser() {
1563 let mut parser = JsonStreamParser::new();
1564 let count = parser.feed(r#"[{"a":1},{"b":2}]"#);
1565 assert_eq!(count, 2);
1566 let results = parser.take_results();
1567 assert_eq!(results.len(), 2);
1568 assert_eq!(results[0].get("a").unwrap().as_f64(), Some(1.0));
1569 assert_eq!(results[1].get("b").unwrap().as_f64(), Some(2.0));
1570 }
1571
1572 #[test]
1573 fn test_streaming_parser_incremental() {
1574 let mut parser = JsonStreamParser::new();
1575 parser.feed("[42,");
1576 assert_eq!(parser.result_count(), 1);
1577 parser.feed("43]");
1578 assert_eq!(parser.result_count(), 2);
1579 let results = parser.take_results();
1580 assert_eq!(results[0].as_f64(), Some(42.0));
1581 assert_eq!(results[1].as_f64(), Some(43.0));
1582 }
1583
1584 #[test]
1585 fn test_records_to_json() {
1586 let records = vec![
1587 vec![
1588 ("id".into(), JsonValue::Number(1.0)),
1589 ("name".into(), JsonValue::Str("a".into())),
1590 ],
1591 vec![
1592 ("id".into(), JsonValue::Number(2.0)),
1593 ("name".into(), JsonValue::Str("b".into())),
1594 ],
1595 ];
1596 let j = records_to_json(&records);
1597 let arr = j.as_array().unwrap();
1598 assert_eq!(arr.len(), 2);
1599 assert_eq!(arr[0].get("id").unwrap().as_f64(), Some(1.0));
1600 }
1601
1602 #[test]
1603 fn test_json_to_records() {
1604 let s = r#"[{"id":1},{"id":2}]"#;
1605 let j = JsonValue::from_str(s).unwrap();
1606 let records = json_to_records(&j).unwrap();
1607 assert_eq!(records.len(), 2);
1608 }
1609
1610 #[test]
1611 fn test_pretty_print() {
1612 let j = JsonValue::from_str(r#"{"a":1,"b":[2,3]}"#).unwrap();
1613 let pretty = j.to_json_pretty(2);
1614 assert!(pretty.contains('\n'));
1615 assert!(pretty.contains("\"a\""));
1616 let reparsed = JsonValue::from_str(&pretty).unwrap();
1618 assert_eq!(reparsed.get("a").unwrap().as_f64(), Some(1.0));
1619 }
1620
1621 #[test]
1622 fn test_as_bool() {
1623 assert_eq!(JsonValue::Bool(true).as_bool(), Some(true));
1624 assert_eq!(JsonValue::Number(1.0).as_bool(), None);
1625 }
1626
1627 #[test]
1628 fn test_as_object() {
1629 let j = JsonValue::from_str(r#"{"a":1}"#).unwrap();
1630 assert!(j.as_object().is_some());
1631 assert!(JsonValue::Number(1.0).as_object().is_none());
1632 }
1633
1634 #[test]
1635 fn test_matrix_to_json() {
1636 let m = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
1637 let j = matrix_to_json(&m);
1638 let arr = j.as_array().unwrap();
1639 assert_eq!(arr.len(), 2);
1640 assert_eq!(arr[0].get_index(0).unwrap().as_f64(), Some(1.0));
1641 assert_eq!(arr[1].get_index(1).unwrap().as_f64(), Some(4.0));
1642 }
1643
1644 #[test]
1647 fn test_streaming_writer_empty_array() {
1648 let mut w = JsonStreamingWriter::new();
1649 w.begin_array();
1650 w.end_array();
1651 let s = w.finish();
1652 assert_eq!(s, "[]");
1653 }
1654
1655 #[test]
1656 fn test_streaming_writer_single_value() {
1657 let mut w = JsonStreamingWriter::new();
1658 w.begin_array();
1659 w.write_value(&JsonValue::Number(42.0));
1660 w.end_array();
1661 let s = w.finish();
1662 assert_eq!(
1663 JsonValue::from_str(&s).unwrap(),
1664 JsonValue::Array(vec![JsonValue::Number(42.0)])
1665 );
1666 }
1667
1668 #[test]
1669 fn test_streaming_writer_multiple_values() {
1670 let mut w = JsonStreamingWriter::new();
1671 w.begin_array();
1672 w.write_value(&JsonValue::Number(1.0));
1673 w.write_value(&JsonValue::Number(2.0));
1674 w.write_value(&JsonValue::Str("three".to_string()));
1675 w.end_array();
1676 let s = w.finish();
1677 let parsed = JsonValue::from_str(&s).unwrap();
1678 let arr = parsed.as_array().unwrap();
1679 assert_eq!(arr.len(), 3);
1680 }
1681
1682 #[test]
1683 fn test_streaming_writer_nested_object() {
1684 let mut w = JsonStreamingWriter::new();
1685 w.begin_object();
1686 w.write_key("x");
1687 w.write_value(&JsonValue::Number(1.0));
1688 w.write_key("y");
1689 w.write_value(&JsonValue::Number(2.0));
1690 w.end_object();
1691 let s = w.finish();
1692 let parsed = JsonValue::from_str(&s).unwrap();
1693 assert_eq!(parsed.get("x").unwrap().as_f64(), Some(1.0));
1694 }
1695
1696 #[test]
1699 fn test_schema_from_number() {
1700 let v = JsonValue::Number(42.0);
1701 let schema = JsonSchema::infer_from(&v);
1702 assert!(schema.validate(&JsonValue::Number(1.0)).is_ok());
1703 assert!(schema.validate(&JsonValue::Str("nope".into())).is_err());
1704 }
1705
1706 #[test]
1707 fn test_schema_from_object() {
1708 let j = JsonValue::from_str(r#"{"name":"alice","age":30}"#).unwrap();
1709 let schema = JsonSchema::infer_from(&j);
1710 let valid = JsonValue::from_str(r#"{"name":"bob","age":25}"#).unwrap();
1711 assert!(schema.validate(&valid).is_ok());
1712 }
1713
1714 #[test]
1715 fn test_schema_from_array() {
1716 let j = JsonValue::from_str("[1,2,3]").unwrap();
1717 let schema = JsonSchema::infer_from(&j);
1718 let valid = JsonValue::from_str("[4,5,6]").unwrap();
1719 assert!(schema.validate(&valid).is_ok());
1720 let invalid = JsonValue::from_str(r#"["a","b"]"#).unwrap();
1721 assert!(schema.validate(&invalid).is_err());
1722 }
1723
1724 #[test]
1725 fn test_schema_to_string() {
1726 let schema = JsonSchema::Number;
1727 let s = schema.to_schema_string();
1728 assert!(s.contains("number"));
1729 }
1730
1731 #[test]
1734 fn test_json_patch_add() {
1735 let mut doc = JsonValue::from_str(r#"{"a":1}"#).unwrap();
1736 let patch = JsonPatch::Add {
1737 path: "b".to_string(),
1738 value: JsonValue::Number(2.0),
1739 };
1740 patch.apply(&mut doc).unwrap();
1741 assert_eq!(doc.get("b").unwrap().as_f64(), Some(2.0));
1742 }
1743
1744 #[test]
1745 fn test_json_patch_remove() {
1746 let mut doc = JsonValue::from_str(r#"{"a":1,"b":2}"#).unwrap();
1747 let patch = JsonPatch::Remove {
1748 path: "b".to_string(),
1749 };
1750 patch.apply(&mut doc).unwrap();
1751 assert!(doc.get("b").is_none());
1752 assert_eq!(doc.get("a").unwrap().as_f64(), Some(1.0));
1753 }
1754
1755 #[test]
1756 fn test_json_patch_replace() {
1757 let mut doc = JsonValue::from_str(r#"{"a":1}"#).unwrap();
1758 let patch = JsonPatch::Replace {
1759 path: "a".to_string(),
1760 value: JsonValue::Number(99.0),
1761 };
1762 patch.apply(&mut doc).unwrap();
1763 assert_eq!(doc.get("a").unwrap().as_f64(), Some(99.0));
1764 }
1765
1766 #[test]
1767 fn test_json_patch_sequence() {
1768 let mut doc = JsonValue::from_str(r#"{"x":1}"#).unwrap();
1769 let patches = vec![
1770 JsonPatch::Add {
1771 path: "y".to_string(),
1772 value: JsonValue::Number(2.0),
1773 },
1774 JsonPatch::Replace {
1775 path: "x".to_string(),
1776 value: JsonValue::Str("hello".into()),
1777 },
1778 ];
1779 apply_patch_sequence(&mut doc, &patches).unwrap();
1780 assert_eq!(doc.get("y").unwrap().as_f64(), Some(2.0));
1781 assert_eq!(doc.get("x").unwrap().as_str(), Some("hello"));
1782 }
1783
1784 #[test]
1787 fn test_jsonpath_root() {
1788 let j = JsonValue::from_str(r#"{"a":1}"#).unwrap();
1789 let results = jsonpath_query(&j, "$");
1790 assert_eq!(results.len(), 1);
1791 assert_eq!(results[0].get("a").unwrap().as_f64(), Some(1.0));
1792 }
1793
1794 #[test]
1795 fn test_jsonpath_child() {
1796 let j = JsonValue::from_str(r#"{"a":{"b":42}}"#).unwrap();
1797 let results = jsonpath_query(&j, "$.a.b");
1798 assert_eq!(results.len(), 1);
1799 assert_eq!(results[0].as_f64(), Some(42.0));
1800 }
1801
1802 #[test]
1803 fn test_jsonpath_array_index() {
1804 let j = JsonValue::from_str("[10,20,30]").unwrap();
1805 let results = jsonpath_query(&j, "$[1]");
1806 assert_eq!(results.len(), 1);
1807 assert_eq!(results[0].as_f64(), Some(20.0));
1808 }
1809
1810 #[test]
1811 fn test_jsonpath_wildcard() {
1812 let j = JsonValue::from_str(r#"{"a":1,"b":2,"c":3}"#).unwrap();
1813 let results = jsonpath_query(&j, "$.*");
1814 assert_eq!(results.len(), 3);
1815 }
1816
1817 #[test]
1818 fn test_jsonpath_array_wildcard() {
1819 let j = JsonValue::from_str("[1,2,3]").unwrap();
1820 let results = jsonpath_query(&j, "$[*]");
1821 assert_eq!(results.len(), 3);
1822 }
1823
1824 #[test]
1827 fn test_json_compress_removes_whitespace() {
1828 let pretty = r#"{
1829 "a": 1,
1830 "b": [
1831 2,
1832 3
1833 ]
1834}"#;
1835 let compressed = json_compress(pretty);
1836 assert!(
1837 !compressed.contains('\n'),
1838 "compressed should not contain newlines"
1839 );
1840 assert!(
1841 !compressed.contains(" "),
1842 "compressed should not contain double spaces"
1843 );
1844 let parsed = JsonValue::from_str(&compressed).unwrap();
1845 assert_eq!(parsed.get("a").unwrap().as_f64(), Some(1.0));
1846 }
1847
1848 #[test]
1849 fn test_json_compress_roundtrip() {
1850 let j = JsonValue::from_str(r#"{"x":1,"y":[2,3],"z":true}"#).unwrap();
1851 let pretty = j.to_json_pretty(4);
1852 let compressed = json_compress(&pretty);
1853 let reparsed = JsonValue::from_str(&compressed).unwrap();
1854 assert_eq!(reparsed, j);
1855 }
1856
1857 #[test]
1858 fn test_json_compress_stats() {
1859 let pretty = r#"{
1860 "alpha": 1,
1861 "beta": 2
1862}"#;
1863 let stats = JsonCompressionStats::compute(pretty);
1864 assert!(
1865 stats.original_len > stats.compressed_len,
1866 "compression should reduce size: {} > {}",
1867 stats.original_len,
1868 stats.compressed_len
1869 );
1870 assert!(stats.compression_ratio() < 1.0);
1871 }
1872
1873 #[test]
1874 fn test_json_compress_preserves_strings_with_spaces() {
1875 let j = JsonValue::from_str(r#"{"msg":"hello world"}"#).unwrap();
1876 let pretty = j.to_json_pretty(2);
1877 let compressed = json_compress(&pretty);
1878 let reparsed = JsonValue::from_str(&compressed).unwrap();
1879 assert_eq!(reparsed.get("msg").unwrap().as_str(), Some("hello world"));
1880 }
1881}