elph_ai/utils/
json_parse.rs1use serde_json::Value;
2
3const VALID_JSON_ESCAPES: &[char] = &['"', '\\', '/', 'b', 'f', 'n', 'r', 't', 'u'];
4
5fn is_control_character(ch: char) -> bool {
6 let code = ch as u32;
7 code <= 0x1f
8}
9
10fn escape_control_character(ch: char) -> String {
11 match ch {
12 '\x08' => "\\b".to_string(),
13 '\x0C' => "\\f".to_string(),
14 '\n' => "\\n".to_string(),
15 '\r' => "\\r".to_string(),
16 '\t' => "\\t".to_string(),
17 _ => format!("\\u{:04x}", ch as u32),
18 }
19}
20
21pub fn repair_json(json: &str) -> String {
23 let mut repaired = String::new();
24 let mut in_string = false;
25 let mut chars = json.chars().peekable();
26
27 while let Some(ch) = chars.next() {
28 if !in_string {
29 repaired.push(ch);
30 if ch == '"' {
31 in_string = true;
32 }
33 continue;
34 }
35
36 if ch == '"' {
37 repaired.push(ch);
38 in_string = false;
39 continue;
40 }
41
42 if ch == '\\' {
43 if let Some(&next) = chars.peek() {
44 if next == 'u' {
45 let unicode: String = chars.by_ref().take(5).collect();
46 if unicode.len() == 5 && unicode[1..].chars().all(|c| c.is_ascii_hexdigit()) {
47 repaired.push_str(&unicode);
48 continue;
49 }
50 }
51 if VALID_JSON_ESCAPES.contains(&next) {
52 repaired.push(ch);
53 repaired.push(chars.next().unwrap());
54 continue;
55 }
56 }
57 repaired.push_str("\\\\");
58 continue;
59 }
60
61 if is_control_character(ch) {
62 repaired.push_str(&escape_control_character(ch));
63 } else {
64 repaired.push(ch);
65 }
66 }
67
68 repaired
69}
70
71pub fn parse_json_with_repair(json: &str) -> Result<Value, serde_json::Error> {
72 match serde_json::from_str::<Value>(json) {
73 Ok(v) => Ok(v),
74 Err(e) => {
75 let repaired = repair_json(json);
76 if repaired != json {
77 serde_json::from_str(&repaired)
78 } else {
79 Err(e)
80 }
81 }
82 }
83}
84
85pub fn parse_streaming_json(partial_json: Option<&str>) -> Value {
87 let partial_json = partial_json.unwrap_or("").trim();
88 if partial_json.is_empty() {
89 return Value::Object(serde_json::Map::new());
90 }
91
92 if let Ok(v) = parse_json_with_repair(partial_json) {
93 return v;
94 }
95
96 for suffix in ["", "}", "]}", "\"}", "\":\"\"}", "]}"] {
98 let attempt = format!("{partial_json}{suffix}");
99 if let Ok(v) = parse_json_with_repair(&attempt) {
100 return v;
101 }
102 }
103
104 Value::Object(serde_json::Map::new())
105}