json_streamparse_rs/lib.rs
1//! # json-streamparse-rs
2//!
3//! Streaming JSON balance detector. Feed bytes incrementally and ask
4//! whether the buffer currently holds a complete top-level JSON value.
5//!
6//! This is *not* a full parser; it's the small utility you want when an
7//! LLM is producing JSON token-by-token and you need to know "can I
8//! hand this to `serde_json::from_str` yet?" without actually parsing
9//! every prefix.
10//!
11//! String-aware (won't be fooled by `{` inside a string literal),
12//! escape-aware (`\\\"` doesn't end the string).
13//!
14//! ## Example
15//!
16//! ```
17//! use json_streamparse_rs::Balancer;
18//! let mut b = Balancer::new();
19//! b.push(b"{\"name\":\"Cl");
20//! assert!(!b.complete());
21//! b.push(b"aude\",\"v\":1}");
22//! assert!(b.complete());
23//! ```
24
25#![deny(missing_docs)]
26
27/// Streaming JSON balance detector.
28#[derive(Debug, Default, Clone)]
29pub struct Balancer {
30 depth: i32,
31 started: bool,
32 in_string: bool,
33 escape: bool,
34 bytes_consumed: u64,
35}
36
37impl Balancer {
38 /// Empty detector.
39 pub fn new() -> Self {
40 Self::default()
41 }
42
43 /// Feed bytes. Updates internal state in place.
44 pub fn push(&mut self, bytes: &[u8]) {
45 for &b in bytes {
46 self.bytes_consumed += 1;
47 if self.in_string {
48 if self.escape {
49 self.escape = false;
50 } else if b == b'\\' {
51 self.escape = true;
52 } else if b == b'"' {
53 self.in_string = false;
54 }
55 continue;
56 }
57 match b {
58 b'{' | b'[' => {
59 self.depth += 1;
60 self.started = true;
61 }
62 b'}' | b']' => {
63 if self.depth > 0 {
64 self.depth -= 1;
65 }
66 }
67 b'"' => {
68 self.in_string = true;
69 self.started = true;
70 }
71 b' ' | b'\t' | b'\n' | b'\r' => {}
72 _ => {
73 self.started = true;
74 }
75 }
76 }
77 }
78
79 /// True when the input so far is non-empty and bracket-balanced
80 /// (depth = 0) and not currently mid-string.
81 pub fn complete(&self) -> bool {
82 self.started && self.depth == 0 && !self.in_string
83 }
84
85 /// Current bracket depth (0 at the root).
86 pub fn depth(&self) -> i32 {
87 self.depth
88 }
89
90 /// Bytes consumed so far.
91 pub fn bytes_consumed(&self) -> u64 {
92 self.bytes_consumed
93 }
94
95 /// Reset to empty.
96 pub fn reset(&mut self) {
97 *self = Self::new();
98 }
99}