json_repair/
repair_json_truncated_boolean_behavior.rs1crate::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 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 !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 if inside_string {
83 output.push('"');
84 changed = true;
85 }
86
87 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}