valu3/to/
json.rs

1use crate::prelude::*;
2use regex::Regex;
3
4/// An enum representing the JSON output format mode.
5pub enum JsonMode {
6    /// Outputs the JSON in an indented format.
7    Indented,
8    /// Outputs the JSON in an inline format.
9    Inline,
10}
11
12impl Value {
13    /// Converts a `Value` into a JSON string.
14    ///
15    /// # Arguments
16    ///
17    /// * `mode` - A `JsonMode` value representing the JSON output format mode.
18    ///
19    /// # Examples
20    ///
21    /// ```no_run
22    /// use json_utils::{Value, JsonMode};
23    ///
24    /// let value = Value::payload_to_value("{\"name\":\"John Doe\",\"age\":30,\"is_active\":true}").unwrap();
25    /// let json_string = value.to_json(JsonMode::Indented);
26    /// println!("{}", json_string);
27    /// ```
28    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    /// Converts the inline JSON string into an indented JSON string.
38    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    /// Generates tab indentation.
45    fn tabs(total: i32) -> String {
46        vec!["\t"; total as usize].join("")
47    }
48
49    /// Converts a `Value` into a JSON string.
50    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}