1use serde_json::{Value, Map};
2
3pub struct Toon;
6
7impl Toon {
8 pub fn serialize(value: &Value) -> String {
10 match value {
11 Value::Object(map) => Self::serialize_object(map, 0),
12 Value::Array(arr) => Self::serialize_array(arr, 0),
13 Value::String(s) => s.clone(),
14 Value::Number(n) => n.to_string(),
15 Value::Bool(b) => b.to_string(),
16 Value::Null => "~".to_string(),
17 }
18 }
19
20 fn serialize_object(map: &Map<String, Value>, indent: usize) -> String {
21 let mut out = String::new();
22 let pad = " ".repeat(indent);
23
24 for (k, v) in map {
25 match v {
26 Value::Object(child_map) => {
27 out.push_str(&format!("{}{}:\n{}", pad, k, Self::serialize_object(child_map, indent + 1)));
28 }
29 Value::Array(arr) => {
30 out.push_str(&format!("{}{}[{}]:\n{}", pad, k, arr.len(), Self::serialize_array(arr, indent + 1)));
31 }
32 _ => {
33 out.push_str(&format!("{}{}: {}\n", pad, k, Self::serialize(v)));
34 }
35 }
36 }
37 out
38 }
39
40 fn serialize_array(arr: &[Value], indent: usize) -> String {
41 if arr.is_empty() {
42 return "[]".to_string();
43 }
44
45 if let Some(first) = arr.first() {
47 if let Value::Object(first_map) = first {
48 let keys: Vec<String> = first_map.keys().cloned().collect();
49 let mut out = format!("{{{}}}:\n", keys.join(","));
50 let pad = " ".repeat(indent);
51
52 for item in arr {
53 if let Value::Object(item_map) = item {
54 let values: Vec<String> = keys.iter()
55 .map(|k| item_map.get(k).map(|v| Self::serialize_flat(v)).unwrap_or_else(|| "~".to_string()))
56 .collect();
57 out.push_str(&format!("{}{}\n", pad, values.join(",")));
58 }
59 }
60 return out;
61 }
62 }
63
64 let mut out = String::new();
66 let pad = " ".repeat(indent);
67 for v in arr {
68 out.push_str(&format!("{}- {}\n", pad, Self::serialize(v).trim()));
69 }
70 out
71 }
72
73 fn serialize_flat(value: &Value) -> String {
74 match value {
75 Value::String(s) => s.replace(',', "\\,").to_string(),
76 Value::Number(n) => n.to_string(),
77 Value::Bool(b) => if *b { "T" } else { "F" }.to_string(),
78 _ => ".".to_string(),
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use serde_json::json;
87
88 #[test]
89 fn test_toon_tabular() {
90 let data = json!([
91 {"id": 1, "name": "Apple", "price": 10},
92 {"id": 2, "name": "Banana", "price": 5}
93 ]);
94 let toon = Toon::serialize(&data);
95 assert!(toon.contains("{id,name,price}"));
96 assert!(toon.contains("1,Apple,10"));
97 }
98
99 #[test]
100 fn test_toon_object() {
101 let data = json!({
102 "user": "admin",
103 "meta": { "last_login": "2024-01-01" }
104 });
105 let toon = Toon::serialize(&data);
106 assert!(toon.contains("user: admin"));
107 assert!(toon.contains("meta:"));
108 }
109}