1use crate::prelude::*;
2use regex::Regex;
3
4pub enum JsonMode {
6 Indented,
8 Inline,
10}
11
12impl Value {
13 pub fn to_json(&self, mode: JsonMode) -> String {
29 let value = Value::to_json_inner(self, 0);
30
31 match mode {
32 JsonMode::Inline => Self::inline(value),
33 JsonMode::Indented => value,
34 }
35 }
36
37 fn inline(value: String) -> String {
39 let re = Regex::new(r"(\n)|(\t)").unwrap();
40 let result = re.replace_all(&value, "");
41 result.to_string()
42 }
43
44 fn tabs(total: i32) -> String {
46 vec!["\t"; total as usize].join("")
47 }
48
49 fn to_json_inner(val: &Value, children: i32) -> String {
51 match val {
52 Value::Object(o) => {
53 let contents: Vec<_> = o
54 .iter()
55 .map(|(name, value)| {
56 format!(
57 "\n\t{}\"{}\": {}",
58 &Self::tabs(children),
59 name,
60 Value::to_json_inner(value, children + 1)
61 )
62 })
63 .collect();
64 format!("{{{}\n{}}}", contents.join(","), &Self::tabs(children))
65 }
66 Value::Array(a) => {
67 let contents: Vec<_> = a
68 .into_iter()
69 .map(|value| Value::to_json_inner(value, children + 1))
70 .collect();
71 format!(
72 "[\n\t{}{}\n{}]",
73 &Self::tabs(children),
74 contents.join(&format!(",\n\t{}", &Self::tabs(children))),
75 &Self::tabs(children)
76 )
77 }
78 Value::String(s) => {
79 let string = s.as_str();
80 let re = Regex::new(r#"""#).unwrap();
81 let list = string
82 .chars()
83 .into_iter()
84 .map(|c| c.to_string())
85 .collect::<Vec<_>>();
86 let mut result = list.clone();
87 let mut add_posi = 0;
88
89 for item in re.captures_iter(string) {
90 let range = item.get(0).unwrap().range();
91
92 if range.start.eq(&0) {
93 result.insert(range.start + add_posi, r#"\"#.to_string());
94 add_posi += 1;
95 } else {
96 let before = range.start - 1;
97
98 if let Some(prev_char) = list.get(before) {
99 if prev_char.ne(r#"\"#) {
100 result.insert(range.start + add_posi, r#"\"#.to_string());
101 add_posi += 1;
102 }
103 }
104 }
105 }
106
107 format!("\"{}\"", result.join(""))
108 }
109 Value::Number(n) => format!("{}", n),
110 Value::Boolean(b) => format!("{}", b),
111 Value::Null => "null".to_string(),
112 Value::Undefined => "undefined".to_string(),
113 Value::DateTime(date_time) => format!("\"{}\"", date_time),
114 }
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn it_should_remove_tabs_and_empty_lines() {
124 let str =
125 String::from("{\n\t\"name\":\"John Doe\",\n\t\"age\":30,\n\t\"is_active\":true\n}");
126 let expected = String::from("{\"name\":\"John Doe\",\"age\":30,\"is_active\":true}");
127 assert_eq!(expected, Value::inline(str));
128 }
129
130 #[test]
131 fn it_should_add_tabs_by_number() {
132 assert_eq!("\t\t\t", Value::tabs(3));
133 }
134
135 #[test]
136 fn it_should_convert_a_value_to_json_string() {
137 let value_str = Value::json_to_value("{\"name\":\"John Doe\"}").unwrap();
138 let value_number = Value::json_to_value("{\"age\":30}").unwrap();
139 let value_boolean = Value::json_to_value("{\"is_active\":true}").unwrap();
140 assert_eq!(
141 "{\n\t\"name\": \"John Doe\"\n}",
142 value_str.to_json(JsonMode::Indented)
143 );
144 assert_eq!(
145 "{\n\t\"age\": 30\n}",
146 value_number.to_json(JsonMode::Indented)
147 );
148 assert_eq!(
149 "{\n\t\"is_active\": true\n}",
150 value_boolean.to_json(JsonMode::Indented)
151 )
152 }
153}