json_repair/
repair_json_truncated_boolean_behavior.rs

1crate::ix!();
2
3pub fn repair_json_truncated_boolean_behavior(input: &str) -> Result<String, JsonRepairError> {
4    let mut output        = String::new();
5    let mut chars         = input.chars().peekable();
6    let mut inside_string = false;
7    let mut last_token    = String::new();
8    let mut open_braces   = 0;
9    let mut open_brackets = 0;
10    let mut changed       = false;
11
12    while let Some(c) = chars.next() {
13        output.push(c);
14
15        if c == '"' {
16            // Check if the quote is escaped
17            let mut backslash_count = 0;
18
19            if output.len() >= 2 {
20                let mut idx = output.len() - 2; 
21                loop {
22                    if output.as_bytes()[idx] == b'\\' {
23                        backslash_count += 1;
24                        if idx == 0 {
25                            break;
26                        } else {
27                            idx -= 1;
28                        }
29                    } else {
30                        break;
31                    }
32                }
33            }
34
35            if backslash_count % 2 == 0 {
36                inside_string = !inside_string;
37            }
38            last_token.clear();
39        } else if !inside_string {
40            match c {
41                '{' => open_braces += 1,
42                '}' => {
43                    if open_braces > 0 {
44                        open_braces -= 1;
45                    }
46                }
47                '[' => open_brackets += 1,
48                ']' => {
49                    if open_brackets > 0 {
50                        open_brackets -= 1;
51                    }
52                }
53                _ => {}
54            }
55
56            if c.is_alphabetic() {
57                last_token.push(c);
58            } else {
59                last_token.clear();
60            }
61        }
62    }
63
64    // If we ended with an incomplete boolean
65    if !inside_string {
66        let mut appended = String::new();
67        if last_token == "t" || last_token == "tr" || last_token == "tru" {
68            let remaining = &"true"[last_token.len()..];
69            appended.push_str(remaining);
70        } else if last_token == "f" || last_token == "fa" || last_token == "fal" || last_token == "fals" {
71            let remaining = &"false"[last_token.len()..];
72            appended.push_str(remaining);
73        }
74
75        if !appended.is_empty() {
76            output.push_str(&appended);
77            changed = true;
78        }
79    }
80
81    // Close any unclosed strings
82    if inside_string {
83        output.push('"');
84        changed = true;
85    }
86
87    // Close any unclosed brackets and braces
88    for _ in 0..open_brackets {
89        output.push(']');
90        changed = true;
91    }
92    for _ in 0..open_braces {
93        output.push('}');
94        changed = true;
95    }
96
97    if changed {
98        info!("Repaired truncated booleans or unclosed delimiters in JSON.");
99    }
100
101    Ok(output)
102}
103
104#[cfg(test)]
105mod tests {
106    use super::*;
107    use serde_json::json;
108
109    fn assert_expected_matches_output_result(input: &str, output: &str, expected: &serde_json::Value) {
110        let parsed_output: serde_json::Value = serde_json::from_str(output).expect("Failed to parse output JSON");
111        assert_eq!(&parsed_output, expected, "Parsed output does not match expected value");
112    }
113
114    #[traced_test]
115    fn test_truncated_boolean_true() -> Result<(),JsonRepairError> {
116        let input = r#"{"bool": tr"#;
117        let output = repair_json_truncated_boolean_behavior(input)?;
118        let expected = json!({"bool": true});
119        assert_expected_matches_output_result(input, &output, &expected);
120        Ok(())
121    }
122
123    #[traced_test]
124    fn test_truncated_boolean_false() -> Result<(),JsonRepairError> {
125        let input = r#"{"bool": fal"#;
126        let output = repair_json_truncated_boolean_behavior(input)?;
127        let expected = json!({"bool": false});
128        assert_expected_matches_output_result(input, &output, &expected);
129        Ok(())
130    }
131}