alith_core/
json.rs

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}