1use crate::utils::json::{parse_json, JsonValue};
2use std::collections::{BTreeMap, HashMap};
3use std::fmt;
4use std::ops::{Index, IndexMut};
5
6pub type Map<K, V> = BTreeMap<K, V>;
7
8#[derive(Debug, Clone, PartialEq)]
9pub enum Value {
10 Null,
11 Bool(bool),
12 Number(f64),
13 String(String),
14 Array(Vec<Value>),
15 Object(Map<String, Value>),
16}
17
18impl fmt::Display for Value {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 f.write_str(&self.to_string_compact())
21 }
22}
23
24impl Value {
25 pub fn as_str(&self) -> Option<&str> {
26 match self {
27 Value::String(s) => Some(s.as_str()),
28 _ => None,
29 }
30 }
31
32 pub fn as_f64(&self) -> Option<f64> {
33 match self {
34 Value::Number(n) => Some(*n),
35 _ => None,
36 }
37 }
38
39 pub fn as_i64(&self) -> Option<i64> {
40 match self {
41 Value::Number(n) => Some(*n as i64),
42 _ => None,
43 }
44 }
45
46 pub fn as_u64(&self) -> Option<u64> {
47 match self {
48 Value::Number(n) if *n >= 0.0 => Some(*n as u64),
49 _ => None,
50 }
51 }
52
53 pub fn as_bool(&self) -> Option<bool> {
54 match self {
55 Value::Bool(b) => Some(*b),
56 _ => None,
57 }
58 }
59
60 pub fn as_array(&self) -> Option<&[Value]> {
61 match self {
62 Value::Array(values) => Some(values.as_slice()),
63 _ => None,
64 }
65 }
66
67 pub fn as_object(&self) -> Option<&Map<String, Value>> {
68 match self {
69 Value::Object(map) => Some(map),
70 _ => None,
71 }
72 }
73
74 pub fn get(&self, key: &str) -> Option<&Value> {
75 if let Value::Object(map) = self {
76 map.get(key)
77 } else {
78 None
79 }
80 }
81
82 pub fn to_string_compact(&self) -> String {
83 let mut out = String::new();
84 self.write_compact(&mut out);
85 out
86 }
87
88 pub fn to_string_pretty(&self) -> String {
89 let mut out = String::new();
90 self.write_pretty(&mut out, 0);
91 out
92 }
93
94 fn write_compact(&self, out: &mut String) {
95 match self {
96 Value::Null => out.push_str("null"),
97 Value::Bool(b) => out.push_str(if *b { "true" } else { "false" }),
98 Value::Number(n) => {
99 if n.fract() == 0.0 {
100 out.push_str(&format!("{}", *n as i64));
101 } else {
102 out.push_str(&format!("{}", n));
103 }
104 }
105 Value::String(s) => {
106 out.push('"');
107 out.push_str(&escape_string(s));
108 out.push('"');
109 }
110 Value::Array(values) => {
111 out.push('[');
112 for (idx, value) in values.iter().enumerate() {
113 if idx > 0 {
114 out.push(',');
115 }
116 value.write_compact(out);
117 }
118 out.push(']');
119 }
120 Value::Object(map) => {
121 out.push('{');
122 for (idx, (key, value)) in map.iter().enumerate() {
123 if idx > 0 {
124 out.push(',');
125 }
126 out.push('"');
127 out.push_str(&escape_string(key));
128 out.push('"');
129 out.push(':');
130 value.write_compact(out);
131 }
132 out.push('}');
133 }
134 }
135 }
136
137 fn write_pretty(&self, out: &mut String, indent: usize) {
138 match self {
139 Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => {
140 out.push_str(&self.to_string_compact());
141 }
142 Value::Array(values) => {
143 out.push('[');
144 if !values.is_empty() {
145 out.push('\n');
146 for (idx, value) in values.iter().enumerate() {
147 if idx > 0 {
148 out.push_str(",\n");
149 }
150 out.push_str(&" ".repeat(indent + 1));
151 value.write_pretty(out, indent + 1);
152 }
153 out.push('\n');
154 out.push_str(&" ".repeat(indent));
155 }
156 out.push(']');
157 }
158 Value::Object(map) => {
159 out.push('{');
160 if !map.is_empty() {
161 out.push('\n');
162 for (idx, (key, value)) in map.iter().enumerate() {
163 if idx > 0 {
164 out.push_str(",\n");
165 }
166 out.push_str(&" ".repeat(indent + 1));
167 out.push('"');
168 out.push_str(&escape_string(key));
169 out.push_str("\": ");
170 value.write_pretty(out, indent + 1);
171 }
172 out.push('\n');
173 out.push_str(&" ".repeat(indent));
174 }
175 out.push('}');
176 }
177 }
178 }
179}
180
181fn escape_string(input: &str) -> String {
182 use std::fmt::Write as _;
187 let mut out = String::with_capacity(input.len());
188 for ch in input.chars() {
189 match ch {
190 '"' => out.push_str("\\\""),
191 '\\' => out.push_str("\\\\"),
192 '\n' => out.push_str("\\n"),
193 '\r' => out.push_str("\\r"),
194 '\t' => out.push_str("\\t"),
195 '\u{08}' => out.push_str("\\b"),
196 '\u{0C}' => out.push_str("\\f"),
197 c if (c as u32) < 0x20 => {
198 let _ = write!(out, "\\u{:04x}", c as u32);
199 }
200 c => out.push(c),
201 }
202 }
203 out
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 fn encode(s: &str) -> String {
211 Value::String(s.to_string()).to_string_compact()
212 }
213
214 #[test]
217 fn escape_string_handles_every_control_byte() {
218 for byte in 0x00u8..0x20 {
219 let original: String = std::char::from_u32(byte as u32).unwrap().to_string();
220 let encoded = encode(&original);
221 let parsed: String = from_str(&encoded).unwrap_or_else(|err| {
223 panic!("byte 0x{byte:02x} encoded as {encoded:?} failed to parse: {err}")
224 });
225 assert_eq!(
226 parsed, original,
227 "byte 0x{byte:02x} did not round-trip (encoded={encoded:?})"
228 );
229 }
230 }
231
232 #[test]
233 fn escape_string_handles_standard_escapes() {
234 assert_eq!(encode("\""), "\"\\\"\"");
235 assert_eq!(encode("\\"), "\"\\\\\"");
236 assert_eq!(encode("\n"), "\"\\n\"");
237 assert_eq!(encode("\r"), "\"\\r\"");
238 assert_eq!(encode("\t"), "\"\\t\"");
239 assert_eq!(encode("\u{08}"), "\"\\b\"");
240 assert_eq!(encode("\u{0C}"), "\"\\f\"");
241 }
242
243 #[test]
244 fn escape_string_handles_mixed_payload() {
245 let input = "name=\"x\"\n\\path\t\x01end";
246 let encoded = encode(input);
247 let parsed: String = from_str(&encoded).expect("mixed payload must parse");
248 assert_eq!(parsed, input);
249 }
250
251 #[test]
257 fn audit_log_preserves_low_control_bytes() {
258 let payload = "collection\x01name\x07with\x1fbells";
259 let encoded = encode(payload);
260
261 assert!(
264 encoded.contains("\\u0001"),
265 "expected \\u0001 escape in {encoded:?}"
266 );
267 assert!(
268 encoded.contains("\\u0007"),
269 "expected \\u0007 escape in {encoded:?}"
270 );
271 assert!(
272 encoded.contains("\\u001f"),
273 "expected \\u001f escape in {encoded:?}"
274 );
275 assert!(
276 !encoded.contains('\x01'),
277 "raw \\x01 must not appear in encoded output"
278 );
279
280 let parsed: String = from_str(&encoded).expect("audit payload must parse");
282 assert_eq!(parsed, payload);
283 }
284}
285
286impl From<JsonValue> for Value {
287 fn from(value: JsonValue) -> Self {
288 match value {
289 JsonValue::Null => Value::Null,
290 JsonValue::Bool(b) => Value::Bool(b),
291 JsonValue::Number(n) => Value::Number(n),
292 JsonValue::String(s) => Value::String(s),
293 JsonValue::Array(values) => Value::Array(values.into_iter().map(Value::from).collect()),
294 JsonValue::Object(entries) => {
295 let mut map = Map::new();
296 for (k, v) in entries {
297 map.insert(k, Value::from(v));
298 }
299 Value::Object(map)
300 }
301 }
302 }
303}
304
305impl Index<&str> for Value {
306 type Output = Value;
307
308 fn index(&self, key: &str) -> &Self::Output {
309 static NULL: Value = Value::Null;
310 match self {
311 Value::Object(map) => map.get(key).unwrap_or(&NULL),
312 _ => &NULL,
313 }
314 }
315}
316
317impl IndexMut<&str> for Value {
318 fn index_mut(&mut self, key: &str) -> &mut Self::Output {
319 match self {
320 Value::Object(map) => map.entry(key.to_string()).or_insert(Value::Null),
321 _ => {
322 *self = Value::Object(Map::new());
323 match self {
324 Value::Object(map) => map.entry(key.to_string()).or_insert(Value::Null),
325 _ => unreachable!(),
326 }
327 }
328 }
329 }
330}
331
332pub trait JsonEncode {
333 fn to_json_value(&self) -> Value;
334}
335
336impl<T: JsonEncode + ?Sized> JsonEncode for &T {
337 fn to_json_value(&self) -> Value {
338 (*self).to_json_value()
339 }
340}
341
342pub trait JsonDecode: Sized {
343 fn from_json_value(value: Value) -> Result<Self, String>;
344}
345
346impl JsonEncode for Value {
347 fn to_json_value(&self) -> Value {
348 self.clone()
349 }
350}
351
352impl JsonDecode for Value {
353 fn from_json_value(value: Value) -> Result<Self, String> {
354 Ok(value)
355 }
356}
357
358impl JsonEncode for bool {
359 fn to_json_value(&self) -> Value {
360 Value::Bool(*self)
361 }
362}
363
364impl JsonEncode for i64 {
365 fn to_json_value(&self) -> Value {
366 Value::Number(*self as f64)
367 }
368}
369
370impl JsonEncode for i32 {
371 fn to_json_value(&self) -> Value {
372 Value::Number(*self as f64)
373 }
374}
375
376impl JsonEncode for u8 {
377 fn to_json_value(&self) -> Value {
378 Value::Number(*self as f64)
379 }
380}
381
382impl JsonEncode for u16 {
383 fn to_json_value(&self) -> Value {
384 Value::Number(*self as f64)
385 }
386}
387
388impl JsonEncode for u32 {
389 fn to_json_value(&self) -> Value {
390 Value::Number(*self as f64)
391 }
392}
393
394impl JsonEncode for u64 {
395 fn to_json_value(&self) -> Value {
396 Value::Number(*self as f64)
397 }
398}
399
400impl JsonEncode for usize {
401 fn to_json_value(&self) -> Value {
402 Value::Number(*self as f64)
403 }
404}
405
406impl JsonEncode for f64 {
407 fn to_json_value(&self) -> Value {
408 Value::Number(*self)
409 }
410}
411
412impl JsonEncode for f32 {
413 fn to_json_value(&self) -> Value {
414 Value::Number(*self as f64)
415 }
416}
417
418impl JsonEncode for String {
419 fn to_json_value(&self) -> Value {
420 Value::String(self.clone())
421 }
422}
423
424impl JsonEncode for &str {
425 fn to_json_value(&self) -> Value {
426 Value::String(self.to_string())
427 }
428}
429
430impl<'a> JsonEncode for std::borrow::Cow<'a, str> {
431 fn to_json_value(&self) -> Value {
432 Value::String(self.to_string())
433 }
434}
435
436impl<T: JsonEncode> JsonEncode for Vec<T> {
437 fn to_json_value(&self) -> Value {
438 Value::Array(self.iter().map(|v| v.to_json_value()).collect())
439 }
440}
441
442impl<T: JsonEncode> JsonEncode for [T] {
443 fn to_json_value(&self) -> Value {
444 Value::Array(self.iter().map(|v| v.to_json_value()).collect())
445 }
446}
447
448impl<T: JsonEncode> JsonEncode for Option<T> {
449 fn to_json_value(&self) -> Value {
450 match self {
451 Some(value) => value.to_json_value(),
452 None => Value::Null,
453 }
454 }
455}
456
457impl<const N: usize> JsonEncode for [u8; N] {
458 fn to_json_value(&self) -> Value {
459 Value::Array(self.iter().map(|b| Value::Number(*b as f64)).collect())
460 }
461}
462
463impl<T: JsonEncode> JsonEncode for HashMap<String, T> {
464 fn to_json_value(&self) -> Value {
465 let mut map = Map::new();
466 for (k, v) in self {
467 map.insert(k.clone(), v.to_json_value());
468 }
469 Value::Object(map)
470 }
471}
472
473impl JsonDecode for String {
474 fn from_json_value(value: Value) -> Result<Self, String> {
475 match value {
476 Value::String(s) => Ok(s),
477 _ => Err("expected string".to_string()),
478 }
479 }
480}
481
482impl JsonDecode for bool {
483 fn from_json_value(value: Value) -> Result<Self, String> {
484 match value {
485 Value::Bool(b) => Ok(b),
486 _ => Err("expected bool".to_string()),
487 }
488 }
489}
490
491impl JsonDecode for u8 {
492 fn from_json_value(value: Value) -> Result<Self, String> {
493 match value {
494 Value::Number(n) => Ok(n as u8),
495 _ => Err("expected number".to_string()),
496 }
497 }
498}
499
500impl JsonDecode for u16 {
501 fn from_json_value(value: Value) -> Result<Self, String> {
502 match value {
503 Value::Number(n) => Ok(n as u16),
504 _ => Err("expected number".to_string()),
505 }
506 }
507}
508
509impl JsonDecode for u32 {
510 fn from_json_value(value: Value) -> Result<Self, String> {
511 match value {
512 Value::Number(n) => Ok(n as u32),
513 _ => Err("expected number".to_string()),
514 }
515 }
516}
517
518impl JsonDecode for u64 {
519 fn from_json_value(value: Value) -> Result<Self, String> {
520 match value {
521 Value::Number(n) => Ok(n as u64),
522 _ => Err("expected number".to_string()),
523 }
524 }
525}
526
527impl JsonDecode for usize {
528 fn from_json_value(value: Value) -> Result<Self, String> {
529 match value {
530 Value::Number(n) => Ok(n as usize),
531 _ => Err("expected number".to_string()),
532 }
533 }
534}
535
536impl JsonDecode for i64 {
537 fn from_json_value(value: Value) -> Result<Self, String> {
538 match value {
539 Value::Number(n) => Ok(n as i64),
540 _ => Err("expected number".to_string()),
541 }
542 }
543}
544
545impl JsonDecode for i32 {
546 fn from_json_value(value: Value) -> Result<Self, String> {
547 match value {
548 Value::Number(n) => Ok(n as i32),
549 _ => Err("expected number".to_string()),
550 }
551 }
552}
553
554impl JsonDecode for f32 {
555 fn from_json_value(value: Value) -> Result<Self, String> {
556 match value {
557 Value::Number(n) => Ok(n as f32),
558 _ => Err("expected number".to_string()),
559 }
560 }
561}
562
563impl<T: JsonDecode> JsonDecode for Vec<T> {
564 fn from_json_value(value: Value) -> Result<Self, String> {
565 match value {
566 Value::Array(values) => values.into_iter().map(T::from_json_value).collect(),
567 _ => Err("expected array".to_string()),
568 }
569 }
570}
571
572impl<T: JsonDecode> JsonDecode for HashMap<String, T> {
573 fn from_json_value(value: Value) -> Result<Self, String> {
574 match value {
575 Value::Object(map) => map
576 .into_iter()
577 .map(|(k, v)| Ok((k, T::from_json_value(v)?)))
578 .collect(),
579 _ => Err("expected object".to_string()),
580 }
581 }
582}
583
584impl<T: JsonDecode> JsonDecode for Option<T> {
585 fn from_json_value(value: Value) -> Result<Self, String> {
586 match value {
587 Value::Null => Ok(None),
588 other => Ok(Some(T::from_json_value(other)?)),
589 }
590 }
591}
592
593impl<const N: usize> JsonDecode for [u8; N] {
594 fn from_json_value(value: Value) -> Result<Self, String> {
595 match value {
596 Value::Array(values) => {
597 if values.len() != N {
598 return Err("invalid array length".to_string());
599 }
600 let mut out = [0u8; N];
601 for (idx, val) in values.into_iter().enumerate() {
602 out[idx] = u8::from_json_value(val)?;
603 }
604 Ok(out)
605 }
606 _ => Err("expected array".to_string()),
607 }
608 }
609}
610
611pub fn to_value<T: JsonEncode + ?Sized>(value: &T) -> Value {
612 value.to_json_value()
613}
614
615pub fn to_string<T: JsonEncode + ?Sized>(value: &T) -> Result<String, String> {
616 Ok(to_value(value).to_string_compact())
617}
618
619pub fn to_string_pretty<T: JsonEncode + ?Sized>(value: &T) -> Result<String, String> {
620 Ok(to_value(value).to_string_pretty())
621}
622
623pub fn to_vec<T: JsonEncode + ?Sized>(value: &T) -> Result<Vec<u8>, String> {
624 Ok(to_string(value)?.into_bytes())
625}
626
627pub fn from_str<T: JsonDecode>(input: &str) -> Result<T, String> {
628 let value = parse_json(input).map(Value::from)?;
629 T::from_json_value(value)
630}
631
632pub fn from_slice<T: JsonDecode>(input: &[u8]) -> Result<T, String> {
633 let s = std::str::from_utf8(input).map_err(|e| e.to_string())?;
634 from_str(s)
635}
636
637pub fn from_value<T: JsonDecode>(value: Value) -> Result<T, String> {
638 T::from_json_value(value)
639}
640
641#[macro_export]
642macro_rules! json {
643 (null) => {
644 $crate::serde_json::Value::Null
645 };
646 ([ $( $elem:expr ),* $(,)? ]) => {
647 $crate::serde_json::Value::Array(vec![ $( $crate::json!($elem) ),* ])
648 };
649 ({ $( $key:literal : $value:expr ),* $(,)? }) => {{
650 let mut map = $crate::serde_json::Map::new();
651 $( map.insert($key.to_string(), $crate::json!($value)); )*
652 $crate::serde_json::Value::Object(map)
653 }};
654 ($other:expr) => {
655 $crate::serde_json::to_value(&$other)
656 };
657}
658
659pub use crate::json;