1use regex::Regex;
2use serde_json::{Error as JsonError, Value};
3use thiserror::Error;
4
5#[derive(Debug, Error)]
6pub enum JsonParseError {
7 #[error("JSON parsing error: {0}")]
8 JsonError(#[from] JsonError),
9 #[error("Missing expected key: {0}")]
10 MissingKey(String),
11}
12
13pub fn parse_partial_json(s: &str) -> Result<Value, JsonParseError> {
14 let mut chars: Vec<char> = s.chars().collect();
15 let mut stack = Vec::new();
16 let mut is_inside_string = false;
17 let mut escaped = false;
18
19 for i in 0..chars.len() {
20 let c = chars[i];
21 if is_inside_string {
22 if c == '"' && !escaped {
23 is_inside_string = false;
24 } else if c == '\n' && !escaped {
25 chars[i] = '\\';
26 chars.insert(i + 1, 'n');
27 }
28 escaped = c == '\\' && !escaped;
29 } else {
30 match c {
31 '"' => {
32 is_inside_string = true;
33 escaped = false;
34 }
35 '{' => stack.push('}'),
36 '[' => stack.push(']'),
37 '}' | ']' => {
38 if let Some(last) = stack.last() {
39 if *last == c {
40 stack.pop();
41 }
42 }
43 }
44 _ => {}
45 }
46 }
47 }
48 let mut completed: String = chars.into_iter().collect();
49 completed.extend(stack.into_iter().rev());
50 serde_json::from_str(&completed)
51 .or_else(|_| serde_json::from_str(s))
52 .map_err(JsonParseError::from)
53}
54
55pub fn parse_json_markdown(text: &str) -> Result<Value, JsonParseError> {
56 let re = Regex::new(r"(?s)```(json)?(.*?)(```|$)").unwrap();
57 let json_str = if let Some(caps) = re.captures(text) {
58 caps[2].trim().to_owned()
59 } else {
60 text.trim().to_owned()
61 };
62 parse_partial_json(&json_str)
63}
64
65pub fn parse_and_check_json_markdown(
66 text: &str,
67 expected_keys: &[&str],
68) -> Result<Value, JsonParseError> {
69 let json_obj = parse_json_markdown(text)?;
70
71 if let Value::Object(map) = &json_obj {
72 for key in expected_keys {
73 if !map.contains_key(*key) {
74 return Err(JsonParseError::MissingKey(key.to_string()));
75 }
76 }
77 }
78
79 Ok(json_obj)
80}