1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! Zero-copy event-stream parser.
//!
//! Tokenize a Ktav document into a linear sequence of [`ParseEvent`]s
//! delivered to a user callback. Object keys and unmodified scalars
//! are borrowed straight from the input string; the events you receive
//! carry `&'a str` slices into the original buffer (only
//! escape-decoded strings and canonical numeric forms are allocated,
//! in a temporary arena).
//!
//! Transient state differs by compound shape. The line-oriented state
//! machine writes multi-line compounds straight into the flat event
//! list; its transient state is one scope frame per open bracket plus
//! the parse-wide dotted-key path table. Inline compounds (`a: {x:
//! 1}`) are staged before publication: the scanner appends their
//! events to a flat per-compound `Vec<Event>` scratch (array items —
//! nested arrays included — land there in source order, each exactly
//! once), keeps one
//! insertion-ordered key table (`IndexMap`) per object scope for
//! duplicate/conflict detection and dotted-key merge (§ 6.3), and
//! stages one flat event block per array that is directly an object
//! member's value (the only array staging there is: dotted re-entry,
//! § 5.3.2, can add merge-pairs to an already-seen key, so member
//! values are emitted from the object's final walk, never at arrival
//! time). Only after the whole compound validates are its
//! events appended — through the parser's reusable per-parse staging
//! buffer when the compound is a key's value. No owned `Value` is
//! built, and events are never published from a half-scanned
//! compound.
//!
//! Internally this is the same path that powers [`crate::from_str`]: the
//! flat-event stream is the hot deserialization route. Exposing it
//! publicly lets callers build their own consumers (custom DOMs,
//! streaming validators, partial extractors) without paying for serde.
//!
//! Dotted keys (`a.b.c: 10`) are resolved at tokenize time into
//! synthetic `Key` + `BeginObject` / … / `EndObject` triples, so a
//! callback never has to know they existed — it sees the same shape as
//! a fully-spelled nested object.
//!
//! ## Example
//!
//! See [`parse_events`] for a runnable example.
pub use ;
pub use parse_events as parse_events_raw;
use Bump;
use crateResult;
use crateEventStream;
/// Parse and, when the parser reports re-opened dotted-key prefixes
/// (spec 0.7 § 5.3.2), fold each re-opened block into the buffer of its
/// first appearance so the one-pass serde `MapAccess` sees a single
/// object per key path. Reopen-free documents take the zero-copy fast
/// path: the raw stream is returned untouched (this is the `from_str`
/// hot route). The trigger is document-global: any reopen count above
/// zero — even one in a branch unrelated to the rest of the document —
/// routes the whole stream through the full rebuild in the `merge`
/// module; the fast path is all-or-nothing.
pub
/// A single token emitted by [`parse_events`].
///
/// Each variant either is unit (compound brackets, `Null`, `Bool`) or
/// carries a `&'a str` slice borrowed from the input buffer. Compound
/// values are bracketed by `BeginObject` / `EndObject` (or
/// `BeginArray` / `EndArray`) pairs in the stream; an [`ParseEvent::Key`]
/// is always immediately followed by its value event (which may itself
/// open a nested compound).
///
/// The event stream mirrors the document's actual root shape (spec
/// § 5.0.1): an implicit Object/Array root is bracketed by its own
/// `BeginObject`/`EndObject` (or array) pair; a whole-document inline
/// compound (§ 5.0.1 rules 2/3) emits exactly its own bracket pair with
/// no extra wrapping; a lone-`{`/`[`-opened multi-line root (rules 4/5)
/// emits its real `Begin` when opened and its real `End` at the matching
/// close. Empty / comments-only documents default to an empty implicit
/// Object root (`BeginObject` / `EndObject`).
///
/// # Dotted-key re-entry (spec 0.7 § 5.3.2)
///
/// A dotted-key Object re-opened after an intervening sibling pair (or
/// explicitly created earlier as `a: { … }`) emits a separate
/// `Key` + `BeginObject` … `EndObject` block at its own document
/// position — the stream never re-opens an already-closed compound.
/// Consumers that build values from the stream MUST merge such blocks
/// under the same key path (the crate's own [`crate::from_str`] does).
///
/// # Numeric scalars (spec 0.5.0)
///
/// `Integer` and `Float` carry the *canonical* textual form of the
/// number literal. Under spec 0.5.0, numeric types are inferred from
/// the scalar body's lexical form (§ 3.6, § 5.2 rules 13-14). The
/// old `:i` / `:f` typed markers are removed. `Integer` holds the
/// canonical base-10 decimal form (via `itoa`), `Float` holds the
/// shortest decimal form (via `ryu`).
/// Tokenize `input` and invoke `callback` with each [`ParseEvent`] in
/// document order.
///
/// Events borrow `&str` slices from `input` where possible (object
/// keys, plain scalars). Multi-line scalar bodies and canonicalized
/// scalars (escape decoding, itoa/ryu numeric reformatting) are
/// allocated in a temporary bump arena owned by this call; their
/// slices live as long as the call itself, which is sufficient
/// because the callback only sees them by reference through
/// `ParseEvent<'_>`.
///
/// The event stream mirrors the document's actual root shape (spec
/// § 5.0.1): implicit roots are bracketed by their Begin/End pair,
/// whole-document inline compounds (§ 5.0.1 rules 2/3) emit exactly
/// their own bracket pair, lone-`{`/`[`-opened roots (rules 4/5) emit
/// their real Begin/End, and empty / comments-only documents default to
/// an empty implicit Object root.
///
/// # Errors
///
/// Returns the same [`crate::Error::Structured`] kinds as
/// [`crate::parse`] / [`crate::from_str`] — invalid keys, duplicate
/// keys, dotted-key conflicts, unbalanced brackets, etc. The callback
/// is not invoked for events past the failure point.
///
/// # Examples
///
/// ```text
/// use ktav::thin::{parse_events, ParseEvent};
/// use std::collections::HashMap;
///
/// let src = "port: 8080\nhost: example.com\n";
///
/// let mut depth = 0_usize;
/// let mut flat: HashMap<String, String> = HashMap::new();
/// let mut last_key: Option<String> = None;
///
/// parse_events(src, |ev| match ev {
/// ParseEvent::BeginObject => depth += 1,
/// ParseEvent::EndObject => depth -= 1,
/// ParseEvent::Key(k) if depth == 1 => last_key = Some(k.to_string()),
/// ParseEvent::Str(s) | ParseEvent::Integer(s) | ParseEvent::Float(s) => {
/// if let Some(k) = last_key.take() {
/// flat.insert(k, s.to_string());
/// }
/// }
/// _ => {}
/// })
/// .unwrap();
///
/// assert_eq!(flat.get("port").map(String::as_str), Some("8080"));
/// assert_eq!(flat.get("host").map(String::as_str), Some("example.com"));
/// ```
/// (See `tests/thin_public.rs::flat_pairs_emit_expected_sequence` for the
/// executed test.)