Skip to main content

tool_parser/
partial_json.rs

1use serde_json::{Map, Value};
2
3use crate::{
4    errors::{ParserError, ParserResult},
5    parsers::helpers::is_complete_json,
6    traits::PartialJsonParser,
7};
8
9/// Parser for incomplete JSON
10pub struct PartialJson {
11    /// Maximum depth for nested structures
12    max_depth: usize,
13    /// Whether to allow incomplete values
14    allow_incomplete: bool,
15}
16
17impl PartialJson {
18    /// Create a new partial JSON parser
19    pub fn new(max_depth: usize, allow_incomplete: bool) -> Self {
20        Self {
21            max_depth,
22            allow_incomplete,
23        }
24    }
25
26    /// Parse potentially incomplete JSON, returning parsed value and consumed bytes
27    ///
28    /// # Arguments
29    /// * `input` - The JSON string to parse
30    /// * `allow_partial_strings` - When false, incomplete strings cause parsing to stop
31    ///   (matches Python's Allow.ALL & ~Allow.STR behavior)
32    pub fn parse_value(
33        &self,
34        input: &str,
35        allow_partial_strings: bool,
36    ) -> ParserResult<(Value, usize)> {
37        let mut parser = Parser::new(
38            input,
39            self.max_depth,
40            self.allow_incomplete,
41            allow_partial_strings,
42        );
43        let value = parser.parse_value(0)?;
44        Ok((value, parser.position))
45    }
46}
47
48impl Default for PartialJson {
49    fn default() -> Self {
50        Self::new(32, true)
51    }
52}
53
54impl PartialJsonParser for PartialJson {
55    fn parse(&self, input: &str) -> ParserResult<(Value, usize)> {
56        // Default to allowing partial strings
57        self.parse_value(input, true)
58    }
59
60    fn is_complete(&self, input: &str) -> bool {
61        is_complete_json(input)
62    }
63
64    fn max_depth(&self) -> usize {
65        self.max_depth
66    }
67}
68
69/// Internal parser state
70struct Parser<'a> {
71    chars: std::iter::Peekable<std::str::Chars<'a>>,
72    position: usize,
73    max_depth: usize,
74    allow_incomplete: bool,
75    allow_partial_strings: bool,
76}
77
78impl<'a> Parser<'a> {
79    fn new(
80        input: &'a str,
81        max_depth: usize,
82        allow_incomplete: bool,
83        allow_partial_strings: bool,
84    ) -> Self {
85        Self {
86            chars: input.chars().peekable(),
87            position: 0,
88            max_depth,
89            allow_incomplete,
90            allow_partial_strings,
91        }
92    }
93
94    fn peek(&mut self) -> Option<char> {
95        self.chars.peek().copied()
96    }
97
98    fn advance(&mut self) {
99        if let Some(c) = self.chars.next() {
100            self.position += c.len_utf8();
101        }
102    }
103
104    fn skip_whitespace(&mut self) {
105        while let Some(ch) = self.peek() {
106            if ch.is_whitespace() {
107                self.advance();
108            } else {
109                break;
110            }
111        }
112    }
113
114    fn parse_value(&mut self, depth: usize) -> ParserResult<Value> {
115        if depth > self.max_depth {
116            return Err(ParserError::DepthExceeded(self.max_depth));
117        }
118
119        self.skip_whitespace();
120
121        match self.peek() {
122            Some('{') => self.parse_object(depth + 1),
123            Some('[') => self.parse_array(depth + 1),
124            Some('"') => self.parse_string(),
125            Some('t') | Some('f') => self.parse_bool(),
126            Some('n') => self.parse_null(),
127            Some(c) if c == '-' || c.is_ascii_digit() => self.parse_number(),
128            _ => {
129                if self.allow_incomplete {
130                    Ok(Value::Null)
131                } else {
132                    Err(ParserError::ParsingFailed("Unexpected character".into()))
133                }
134            }
135        }
136    }
137
138    fn parse_object(&mut self, depth: usize) -> ParserResult<Value> {
139        if depth > self.max_depth {
140            return Err(ParserError::DepthExceeded(self.max_depth));
141        }
142
143        let mut object = Map::new();
144
145        // Consume '{'
146        self.advance();
147        self.skip_whitespace();
148
149        // Check for empty object
150        if self.peek() == Some('}') {
151            self.advance();
152            return Ok(Value::Object(object));
153        }
154
155        loop {
156            // Parse key
157            let key = match self.parse_string() {
158                Ok(Value::String(s)) => s,
159                Err(_) if self.allow_incomplete => {
160                    // Incomplete object
161                    return Ok(Value::Object(object));
162                }
163                Err(e) => return Err(e),
164                _ => return Err(ParserError::ParsingFailed("Expected string key".into())),
165            };
166
167            self.skip_whitespace();
168
169            // Expect ':'
170            if self.peek() != Some(':') {
171                if self.allow_incomplete {
172                    // Add null value for incomplete pair
173                    object.insert(key, Value::Null);
174                    return Ok(Value::Object(object));
175                }
176                return Err(ParserError::ParsingFailed("Expected ':'".into()));
177            }
178            self.advance();
179            self.skip_whitespace();
180
181            // Parse value (keep same depth - we already incremented in parse_object)
182            let value = match self.parse_value(depth) {
183                Ok(v) => v,
184                Err(_) if self.allow_incomplete => {
185                    // When allow_partial_strings is false, don't add the key with Null
186                    // Just return the object without this incomplete key-value pair
187                    // This matches Python's behavior: Allow.ALL & ~Allow.STR
188                    if self.allow_partial_strings {
189                        // Add null for incomplete value
190                        object.insert(key, Value::Null);
191                    }
192                    return Ok(Value::Object(object));
193                }
194                Err(e) => return Err(e),
195            };
196
197            object.insert(key, value);
198            self.skip_whitespace();
199
200            match self.peek() {
201                Some(',') => {
202                    self.advance();
203                    self.skip_whitespace();
204                    // Check for trailing comma
205                    if self.peek() == Some('}') {
206                        self.advance();
207                        return Ok(Value::Object(object));
208                    }
209                }
210                Some('}') => {
211                    self.advance();
212                    return Ok(Value::Object(object));
213                }
214                None if self.allow_incomplete => {
215                    return Ok(Value::Object(object));
216                }
217                _ => {
218                    if self.allow_incomplete {
219                        return Ok(Value::Object(object));
220                    }
221                    return Err(ParserError::ParsingFailed("Expected ',' or '}'".into()));
222                }
223            }
224        }
225    }
226
227    fn parse_array(&mut self, depth: usize) -> ParserResult<Value> {
228        if depth > self.max_depth {
229            return Err(ParserError::DepthExceeded(self.max_depth));
230        }
231
232        let mut array = Vec::new();
233
234        // Consume '['
235        self.advance();
236        self.skip_whitespace();
237
238        // Check for empty array
239        if self.peek() == Some(']') {
240            self.advance();
241            return Ok(Value::Array(array));
242        }
243
244        loop {
245            // Parse value (keep same depth - we already incremented in parse_object)
246            let value = match self.parse_value(depth) {
247                Ok(v) => v,
248                Err(_) if self.allow_incomplete => {
249                    return Ok(Value::Array(array));
250                }
251                Err(e) => return Err(e),
252            };
253
254            array.push(value);
255            self.skip_whitespace();
256
257            match self.peek() {
258                Some(',') => {
259                    self.advance();
260                    self.skip_whitespace();
261                    // Check for trailing comma
262                    if self.peek() == Some(']') {
263                        self.advance();
264                        return Ok(Value::Array(array));
265                    }
266                }
267                Some(']') => {
268                    self.advance();
269                    return Ok(Value::Array(array));
270                }
271                None if self.allow_incomplete => {
272                    return Ok(Value::Array(array));
273                }
274                _ => {
275                    if self.allow_incomplete {
276                        return Ok(Value::Array(array));
277                    }
278                    return Err(ParserError::ParsingFailed("Expected ',' or ']'".into()));
279                }
280            }
281        }
282    }
283
284    fn parse_string(&mut self) -> ParserResult<Value> {
285        if self.peek() != Some('"') {
286            return Err(ParserError::ParsingFailed("Expected '\"'".into()));
287        }
288
289        // Consume opening quote
290        self.advance();
291
292        let mut string = String::new();
293        let mut escaped = false;
294
295        while let Some(ch) = self.peek() {
296            if escaped {
297                // Handle escape sequences
298                let escaped_char = match ch {
299                    '"' | '\\' | '/' => ch,
300                    'b' => '\u{0008}',
301                    'f' => '\u{000C}',
302                    'n' => '\n',
303                    'r' => '\r',
304                    't' => '\t',
305                    'u' => {
306                        // Unicode escape
307                        self.advance();
308                        let hex = self.parse_unicode_escape()?;
309                        string.push(hex);
310                        escaped = false;
311                        continue;
312                    }
313                    _ => ch, // Invalid escape, but be lenient
314                };
315                string.push(escaped_char);
316                escaped = false;
317            } else if ch == '\\' {
318                escaped = true;
319            } else if ch == '"' {
320                // End of string
321                self.advance();
322                return Ok(Value::String(string));
323            } else {
324                string.push(ch);
325            }
326            self.advance();
327        }
328
329        // Incomplete string
330        if self.allow_incomplete && self.allow_partial_strings {
331            Ok(Value::String(string))
332        } else {
333            Err(ParserError::ParsingFailed("Unterminated string".into()))
334        }
335    }
336
337    fn parse_unicode_escape(&mut self) -> ParserResult<char> {
338        let mut hex = String::new();
339        for _ in 0..4 {
340            if let Some(ch) = self.peek() {
341                if ch.is_ascii_hexdigit() {
342                    hex.push(ch);
343                    self.advance();
344                } else {
345                    break;
346                }
347            } else {
348                break;
349            }
350        }
351
352        if hex.len() == 4 {
353            let mut code = u32::from_str_radix(&hex, 16)
354                .map_err(|_| ParserError::ParsingFailed("Invalid unicode escape".into()))?;
355            // A high surrogate is only a char together with the low surrogate escape after it.
356            if (0xD800..=0xDBFF).contains(&code) {
357                let mut ahead = self.chars.clone();
358                if ahead.next() == Some('\\') && ahead.next() == Some('u') {
359                    let low: String = ahead.take(4).collect();
360                    if let Ok(low @ 0xDC00..=0xDFFF) = u32::from_str_radix(&low, 16) {
361                        code = 0x10000 + ((code - 0xD800) << 10) + (low - 0xDC00);
362                        (0..6).for_each(|_| self.advance());
363                    }
364                }
365            }
366            char::from_u32(code)
367                .ok_or_else(|| ParserError::ParsingFailed("Invalid unicode escape".into()))
368        } else if self.allow_incomplete {
369            Ok('\u{FFFD}') // Replacement character
370        } else {
371            Err(ParserError::ParsingFailed(
372                "Incomplete unicode escape".into(),
373            ))
374        }
375    }
376
377    fn parse_number(&mut self) -> ParserResult<Value> {
378        let mut number = String::new();
379
380        // Handle negative sign
381        if self.peek() == Some('-') {
382            number.push('-');
383            self.advance();
384        }
385
386        // Parse integer part
387        if self.peek() == Some('0') {
388            number.push('0');
389            self.advance();
390        } else {
391            while let Some(ch) = self.peek() {
392                if ch.is_ascii_digit() {
393                    number.push(ch);
394                    self.advance();
395                } else {
396                    break;
397                }
398            }
399        }
400
401        // Parse decimal part
402        if self.peek() == Some('.') {
403            number.push('.');
404            self.advance();
405
406            while let Some(ch) = self.peek() {
407                if ch.is_ascii_digit() {
408                    number.push(ch);
409                    self.advance();
410                } else {
411                    break;
412                }
413            }
414        }
415
416        // Parse exponent
417        if let Some(ch) = self.peek() {
418            if ch == 'e' || ch == 'E' {
419                number.push(ch);
420                self.advance();
421
422                if let Some(sign) = self.peek() {
423                    if sign == '+' || sign == '-' {
424                        number.push(sign);
425                        self.advance();
426                    }
427                }
428
429                while let Some(ch) = self.peek() {
430                    if ch.is_ascii_digit() {
431                        number.push(ch);
432                        self.advance();
433                    } else {
434                        break;
435                    }
436                }
437            }
438        }
439
440        // Try to parse as integer first, then as float
441        if let Ok(n) = number.parse::<i64>() {
442            Ok(Value::Number(serde_json::Number::from(n)))
443        } else if let Ok(n) = number.parse::<f64>() {
444            Ok(Value::Number(
445                serde_json::Number::from_f64(n).unwrap_or_else(|| serde_json::Number::from(0)),
446            ))
447        } else if self.allow_incomplete {
448            Ok(Value::Number(serde_json::Number::from(0)))
449        } else {
450            Err(ParserError::ParsingFailed("Invalid number".into()))
451        }
452    }
453
454    fn parse_bool(&mut self) -> ParserResult<Value> {
455        let mut word = String::new();
456
457        // Peek at upcoming characters to validate it looks like a boolean
458        let mut temp_chars = self.chars.clone();
459        while let Some(&ch) = temp_chars.peek() {
460            if ch.is_alphabetic() && word.len() < 5 {
461                // "false" is 5 chars
462                word.push(ch);
463                temp_chars.next();
464            } else {
465                break;
466            }
467        }
468
469        // Check if it's a valid boolean prefix
470        let is_valid = word == "true"
471            || word == "false"
472            || (self.allow_incomplete && ("true".starts_with(&word) || "false".starts_with(&word)));
473
474        if !is_valid {
475            return Err(ParserError::ParsingFailed("Invalid boolean".into()));
476        }
477
478        // Now actually consume the characters
479        word.clear();
480        while let Some(ch) = self.peek() {
481            if ch.is_alphabetic() {
482                word.push(ch);
483                self.advance();
484            } else {
485                break;
486            }
487        }
488
489        match word.as_str() {
490            "true" => Ok(Value::Bool(true)),
491            "false" => Ok(Value::Bool(false)),
492            partial if self.allow_incomplete => {
493                if "true".starts_with(partial) {
494                    Ok(Value::Bool(true))
495                } else if "false".starts_with(partial) {
496                    Ok(Value::Bool(false))
497                } else {
498                    Err(ParserError::ParsingFailed("Invalid boolean".into()))
499                }
500            }
501            _ => Err(ParserError::ParsingFailed("Invalid boolean".into())),
502        }
503    }
504
505    fn parse_null(&mut self) -> ParserResult<Value> {
506        let mut word = String::new();
507
508        // Peek at upcoming characters to validate it looks like "null"
509        let mut temp_chars = self.chars.clone();
510        while let Some(&ch) = temp_chars.peek() {
511            if ch.is_alphabetic() && word.len() < 4 {
512                // "null" is 4 chars
513                word.push(ch);
514                temp_chars.next();
515            } else {
516                break;
517            }
518        }
519
520        // Check if it's a valid null prefix
521        let is_valid = word == "null" || (self.allow_incomplete && "null".starts_with(&word));
522
523        if !is_valid {
524            return Err(ParserError::ParsingFailed("Invalid null".into()));
525        }
526
527        // Now actually consume the characters
528        word.clear();
529        while let Some(ch) = self.peek() {
530            if ch.is_alphabetic() {
531                word.push(ch);
532                self.advance();
533            } else {
534                break;
535            }
536        }
537
538        if word == "null" || (self.allow_incomplete && "null".starts_with(&word)) {
539            Ok(Value::Null)
540        } else {
541            Err(ParserError::ParsingFailed("Invalid null".into()))
542        }
543    }
544}