json_string/
helpers.rs

1use num::Zero;
2
3use crate::public::{
4    parse_json_string::parse_json_string,
5    parse_stringified_json_string::parse_stringified_json_string,
6};
7
8pub(crate) fn json_context(trimmed_str: &str) -> JsonContext {
9    let json_context = match trimmed_str {
10        trimmed_str if trimmed_str.starts_with('{') && trimmed_str.ends_with('}') => {
11            JsonContext::Object
12        }
13        trimmed_str if trimmed_str.starts_with('[') && trimmed_str.ends_with(']') => {
14            JsonContext::Array
15        }
16        _ => JsonContext::Value,
17    };
18
19    json_context
20}
21
22pub(crate) fn ensure_array_wrapper(string: &str) -> String {
23    let array_string = if string.starts_with('[') && string.ends_with(']') {
24        string.to_string()
25    } else {
26        format!("[{string}]")
27    };
28    array_string
29}
30
31#[allow(clippy::needless_pass_by_value)]
32pub(crate) fn content_str(json_context: JsonContext, trimmed_str: &str) -> String {
33    let content_str = match json_context {
34        JsonContext::Array | JsonContext::Object => {
35            let mut content_str = trimmed_str[1..].to_string();
36            content_str.pop();
37
38            let trimmed_content_str = content_str
39                .trim_matches([' ', '\n', '\t', ',', ';', ':'])
40                .trim_start_matches("\\n")
41                .trim_end_matches("\\n")
42                .trim_start_matches("\\\\n")
43                .trim_end_matches("\\\\n")
44                .to_string();
45
46            trimmed_content_str
47        }
48        JsonContext::Value => trimmed_str.to_string(),
49    };
50
51    content_str
52}
53
54#[allow(clippy::needless_pass_by_value)]
55pub(crate) fn rewrap_string(parsed_json_string: &str, json_context: JsonContext) -> String {
56    let rewrapped_string = match json_context {
57        JsonContext::Array => {
58            let rewrapped_string = format!("[{parsed_json_string}]");
59
60            rewrapped_string
61        }
62        JsonContext::Object => {
63            let rewrapped_string = format!("{{{parsed_json_string}}}");
64
65            rewrapped_string
66        }
67        JsonContext::Value => parsed_json_string.to_string(),
68    };
69
70    rewrapped_string
71}
72
73#[derive(Clone, Debug, PartialEq, Eq)]
74pub(crate) enum JsonContext {
75    Array,
76    Object,
77    Value,
78}
79
80pub(crate) fn handle_object_w_wrapper(string: &str) -> String {
81    let mut content_string = string[1..].to_string();
82    content_string.pop();
83    let content_string = content_string
84        .trim_matches([' ', '\n', '\t', ','])
85        .trim_start_matches("\\\\n")
86        .trim_end_matches("\\\\n")
87        .trim_start_matches("\\n")
88        .trim_end_matches("\\n");
89
90    let object_context = JsonContext::Object;
91
92    let new_object_substance = parse_json_string(content_string, object_context);
93
94    let new_with_braces = format!("{{{new_object_substance}}}");
95
96    new_with_braces
97}
98
99pub(crate) fn handle_stringified_object_w_wrapper(string: &str) -> String {
100    let mut content_string = string[1..].to_string();
101    content_string.pop();
102    let content_string = content_string
103        .trim_matches([' ', '\n', '\t', ','])
104        .trim_start_matches("\\\\n")
105        .trim_end_matches("\\\\n")
106        .trim_start_matches("\\n")
107        .trim_end_matches("\\n");
108
109    let object_context = JsonContext::Object;
110
111    let new_object_substance = parse_stringified_json_string(content_string, object_context);
112
113    let new_with_braces = format!("{{{new_object_substance}}}");
114
115    new_with_braces
116}
117
118pub(crate) fn handle_stringified_array_content(string: &str) -> String {
119    let mut array_elements = split_array_elements(string)
120        .iter()
121        .map(|element| {
122            let value_context = JsonContext::Value;
123
124            let new_element = parse_stringified_json_string(element, value_context);
125            let formatted_element = format!("{new_element}, ");
126            formatted_element
127        })
128        .collect::<String>();
129    array_elements.pop();
130    array_elements.pop();
131
132    array_elements
133}
134
135pub(crate) fn handle_array_content(string: &str) -> String {
136    let mut array_elements = split_array_elements(string)
137        .iter()
138        .map(|element| {
139            let value_context = JsonContext::Value;
140
141            let new_element = parse_json_string(element, value_context);
142            let formatted_element = format!("{new_element}, ");
143            formatted_element
144        })
145        .collect::<String>();
146    array_elements.pop();
147    array_elements.pop();
148
149    array_elements
150}
151
152pub(crate) fn handle_stringified_array_w_wrapper(string: &str) -> String {
153    let mut content_str = string[1..].to_string();
154    content_str.pop();
155    let content_str = content_str.trim();
156
157    let mut array_str = split_array_elements(content_str)
158        .iter()
159        .map(|element| {
160            let value_context = JsonContext::Value;
161
162            let new_element = parse_stringified_json_string(element, value_context);
163            let formatted_element = format!("{new_element}, ");
164            formatted_element
165        })
166        .collect::<String>();
167    array_str.pop();
168    array_str.pop();
169
170    let add_the_brackets_back = format!("[{array_str}]");
171
172    add_the_brackets_back
173}
174
175pub(crate) fn handle_array_w_wrapper(string: &str) -> String {
176    let mut content_str = string[1..].to_string();
177    content_str.pop();
178    let content_str = content_str.trim();
179
180    let mut array_str = split_array_elements(content_str)
181        .iter()
182        .map(|element| {
183            let value_context = JsonContext::Value;
184
185            let new_element = parse_json_string(element, value_context);
186            let formatted_element = format!("{new_element}, ");
187            formatted_element
188        })
189        .collect::<String>();
190    array_str.pop();
191    array_str.pop();
192
193    let add_the_brackets_back = format!("[{array_str}]");
194
195    add_the_brackets_back
196}
197
198pub(crate) fn handle_stringified_object_content(string: &str) -> String {
199    let mut key_value_pairs = split_object_elements(string)
200        .iter()
201        .filter_map(|kv_pair| {
202            let (key, value) = kv_pair.split_once(':')?;
203
204            let trimmed_key = key.trim();
205
206            let new_key = if trimmed_key.starts_with('\"') && trimmed_key.ends_with('\"') {
207                trimmed_key.to_string()
208            } else {
209                format!("\"{trimmed_key}\"")
210            };
211
212            let trimmed_value = value
213                .trim_matches([' ', '\n', '\t', ','])
214                .trim_start_matches("\\\\n")
215                .trim_end_matches("\\\\n")
216                .trim_start_matches("\\n")
217                .trim_end_matches("\\n");
218
219            let value_context = JsonContext::Value;
220
221            let new_value = parse_stringified_json_string(trimmed_value, value_context);
222
223            let new_kv_pair = format!("{new_key}: {new_value}, ");
224
225            Some(new_kv_pair)
226        })
227        .collect::<String>();
228    key_value_pairs.pop();
229    key_value_pairs.pop();
230
231    key_value_pairs
232}
233
234pub(crate) fn handle_object_content(string: &str) -> String {
235    let mut key_value_pairs = split_object_elements(string)
236        .iter()
237        .filter_map(|kv_pair| {
238            let (key, value) = kv_pair.split_once(':')?;
239
240            let trimmed_key = key.trim();
241
242            let new_key = if trimmed_key.starts_with('\"') && trimmed_key.ends_with('\"') {
243                trimmed_key.to_string()
244            } else {
245                format!("\"{trimmed_key}\"")
246            };
247
248            let trimmed_value = value
249                .trim_matches([' ', '\n', '\t', ','])
250                .trim_start_matches("\\\\n")
251                .trim_end_matches("\\\\n")
252                .trim_start_matches("\\n")
253                .trim_end_matches("\\n");
254
255            let value_context = JsonContext::Value;
256
257            let new_value = parse_json_string(trimmed_value, value_context);
258
259            let new_kv_pair = format!("{new_key}: {new_value}, ");
260
261            Some(new_kv_pair)
262        })
263        .collect::<String>();
264    key_value_pairs.pop();
265    key_value_pairs.pop();
266
267    key_value_pairs
268}
269
270pub(crate) fn split_array_elements(string: &str) -> Vec<String> {
271    let mut all_elements = Vec::new();
272    let mut current_element = String::default();
273    let mut array_lefts = 0;
274    let mut object_lefts = 0;
275
276    for ch in string.chars() {
277        let is_separator = ch == ',' || ch == ';';
278
279        if is_separator && array_lefts.is_zero() && object_lefts.is_zero() {
280            let trimmed_current_element = current_element
281                .trim_matches([' ', '\n', '\t', ',', ';'])
282                .trim_start_matches("\\\\n")
283                .trim_end_matches("\\\\n")
284                .trim_start_matches("\\n")
285                .trim_end_matches("\\n")
286                .to_string();
287            all_elements.push(trimmed_current_element.clone());
288            current_element.clear();
289        }
290
291        if ch == '{' && array_lefts.is_zero() {
292            object_lefts += 1;
293        }
294
295        if ch == '[' && object_lefts.is_zero() {
296            array_lefts += 1;
297        }
298
299        if ch == ']' && !array_lefts.is_zero() {
300            array_lefts -= 1;
301        }
302
303        if ch == '}' && !object_lefts.is_zero() {
304            object_lefts -= 1;
305        }
306        current_element.push(ch);
307    }
308
309    let trimmed_current_element = current_element
310        .trim_matches([' ', '\n', '\t', ',', ';'])
311        .trim_start_matches("\\\\n")
312        .trim_end_matches("\\\\n")
313        .trim_start_matches("\\n")
314        .trim_end_matches("\\n")
315        .to_string();
316    all_elements.push(trimmed_current_element.clone());
317    current_element.clear();
318
319    all_elements
320}
321
322pub(crate) fn split_object_elements(object_str: &str) -> Vec<String> {
323    let mut all_elements = Vec::new();
324    let mut current_element = String::default();
325    let mut array_lefts = 0;
326    let mut object_lefts = 0;
327    let mut double_quote_lefts = 0;
328
329    for ch in object_str.chars() {
330        let is_separator = ch == ',' || ch == ';';
331        if is_separator
332            && array_lefts.is_zero()
333            && object_lefts.is_zero()
334            && double_quote_lefts.is_zero()
335        {
336            let trimmed_element = current_element
337                .trim_matches([' ', '\n', '\t', ','])
338                .trim_start_matches("\\\\n")
339                .trim_end_matches("\\\\n")
340                .trim_start_matches("\\n")
341                .trim_end_matches("\\n")
342                .to_string();
343            all_elements.push(trimmed_element);
344            current_element.clear();
345        }
346
347        if ch == '{' && array_lefts.is_zero() && double_quote_lefts.is_zero() {
348            object_lefts += 1;
349        }
350
351        if ch == '[' && object_lefts.is_zero() && double_quote_lefts.is_zero() {
352            array_lefts += 1;
353        }
354
355        if ch == '\"' && object_lefts.is_zero() && array_lefts.is_zero() {
356            if double_quote_lefts.is_zero() {
357                double_quote_lefts += 1;
358            } else {
359                double_quote_lefts -= 1;
360            };
361        }
362
363        if ch == ']' && !array_lefts.is_zero() && double_quote_lefts.is_zero() {
364            array_lefts -= 1;
365        }
366
367        if ch == '}' && !object_lefts.is_zero() && double_quote_lefts.is_zero() {
368            object_lefts -= 1;
369        }
370
371        current_element.push(ch);
372    }
373
374    let trimmed_element = current_element
375        .trim_matches([' ', '\n', '\t', ','])
376        .trim_start_matches("\\\\n")
377        .trim_end_matches("\\\\n")
378        .trim_start_matches("\\n")
379        .trim_end_matches("\\n")
380        .to_string();
381    all_elements.push(trimmed_element);
382    current_element.clear();
383
384    all_elements
385}
386
387pub(crate) fn format_stringified_value(value_str: &str) -> String {
388    if value_str.is_empty() {
389        return String::default();
390    }
391
392    let without_quotes = value_str
393        .trim_matches('\"')
394        .trim_start_matches("\\\\n")
395        .trim_end_matches("\\\\n")
396        .trim_start_matches("\\n")
397        .trim_end_matches("\\n");
398    let formatted_value = format!("\"{without_quotes}\"");
399
400    formatted_value
401}
402
403pub(crate) fn format_value(value_str: &str) -> String {
404    let formatted_value = match value_str.to_lowercase().as_str() {
405        value_str
406            if value_str.parse::<bool>().is_ok()
407                || value_str.parse::<f64>().is_ok()
408                || value_str.parse::<u64>().is_ok()
409                || value_str.parse::<i64>().is_ok()
410                || value_str.starts_with('[') && value_str.ends_with(']')
411                || value_str.starts_with('{') && value_str.ends_with('}')
412                || value_str.is_empty() =>
413        {
414            value_str.to_string()
415        }
416        "none" => "None".to_string(),
417        _ => {
418            let without_quotes = value_str
419                .trim_matches('\"')
420                .trim_start_matches("\\\\n")
421                .trim_end_matches("\\\\n")
422                .trim_start_matches("\\n")
423                .trim_end_matches("\\n");
424
425            let with_quotes = format!("\"{without_quotes}\"");
426            with_quotes
427        }
428    };
429
430    formatted_value
431}