• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kaidokert / picojson-rs / 15965929705

30 Jun 2025 06:50AM UTC coverage: 94.022% (+1.4%) from 92.622%
15965929705

Pull #14

github

web-flow
Merge c314a47f2 into 2a9c5eb78
Pull Request #14: Clippy clean and coverage

66 of 72 new or added lines in 6 files covered. (91.67%)

2076 of 2208 relevant lines covered (94.02%)

133.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.7
/picojson/src/pull_parser.rs
1
// SPDX-License-Identifier: Apache-2.0
2

3
use crate::copy_on_escape::CopyOnEscape;
4
use crate::escape_processor::{EscapeProcessor, UnicodeEscapeCollector};
5
use crate::shared::{ContentRange, Event, ParseError, ParserErrorHandler, ParserState, State};
6
use crate::slice_input_buffer::{InputBuffer, SliceInputBuffer};
7
use crate::ujson;
8
use ujson::{EventToken, Tokenizer};
9

10
use ujson::{BitStackConfig, DefaultConfig};
11

12
/// Result of processing a tokenizer event
13
enum EventResult<'a, 'b> {
14
    /// Event processing complete, return this event
15
    Complete(Event<'a, 'b>),
16
    /// Continue processing, no event to return yet
17
    Continue,
18
    /// Extract string content from current state
19
    ExtractString,
20
    /// Extract key content from current state
21
    ExtractKey,
22
    /// Extract number content from current state,
23
    /// bool indicates if number was terminated by container delimiter
24
    ExtractNumber(bool),
25
}
26

27
/// A flexible pull parser for JSON that yields events on demand.
28
/// Generic over BitStack storage type for configurable nesting depth.
29
// Lifetime 'a is the input buffer lifetime
30
// lifetime 'b is the scratch/copy buffer lifetime
31
pub struct PullParser<'a, 'b, C: BitStackConfig = DefaultConfig> {
32
    tokenizer: Tokenizer<C::Bucket, C::Counter>,
33
    buffer: SliceInputBuffer<'a>,
34
    parser_state: ParserState,
35
    copy_on_escape: CopyOnEscape<'a, 'b>,
36
    /// Shared Unicode escape collector for \uXXXX sequences
37
    unicode_escape_collector: UnicodeEscapeCollector,
38
}
39

40
/// Methods for the pull parser.
41
impl<'a> PullParser<'a, '_, DefaultConfig> {
42
    /// Creates a new parser for the given JSON input.
43
    ///
44
    /// This parser assumes no string escapes will be encountered. If escapes are found,
45
    /// parsing will fail with `ScratchBufferFull` error.
46
    ///
47
    /// For JSON with potential string escapes, use `with_buffer()` instead.
48
    ///
49
    /// # Arguments
50
    /// * `input` - A string slice containing the JSON data to be parsed.
51
    ///
52
    /// # Example
53
    /// ```
54
    /// use picojson::PullParser;
55
    /// let parser = PullParser::new(r#"{"name": "value"}"#);
56
    /// ```
57
    pub fn new(input: &'a str) -> Self {
9✔
58
        Self::new_from_slice(input.as_bytes())
9✔
59
    }
9✔
60

61
    /// Creates a new parser from a byte slice.
62
    ///
63
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_buffer_from_slice`].
64
    ///
65
    /// # Example
66
    /// ```
67
    /// # use picojson::PullParser;
68
    /// let parser = PullParser::new_from_slice(br#"{"name": "value"}"#);
69
    /// ```
70
    ///
71
    /// [`with_buffer_from_slice`]: Self::with_buffer_from_slice
72
    pub fn new_from_slice(input: &'a [u8]) -> Self {
9✔
73
        Self::with_config_from_slice(input)
9✔
74
    }
9✔
75
}
76

77
/// Constructor with scratch buffer for PullParser using DefaultConfig
78
impl<'a, 'b> PullParser<'a, 'b, DefaultConfig> {
79
    /// Creates a new parser for the given JSON input with external scratch buffer.
80
    ///
81
    /// Use this when your JSON contains string escapes (like `\n`, `\"`, `\u0041`) that
82
    /// need to be unescaped during parsing.
83
    ///
84
    /// # Arguments
85
    /// * `input` - A string slice containing the JSON data to be parsed.
86
    /// * `scratch_buffer` - A mutable byte slice for temporary string unescaping operations.
87
    ///   This buffer needs to be at least as long as the longest
88
    ///   contiguous token (string, key, number) in the input.
89
    ///
90
    /// # Example
91
    /// ```
92
    /// use picojson::PullParser;
93
    /// let mut scratch = [0u8; 1024];
94
    /// let parser = PullParser::with_buffer(r#"{"msg": "Hello\nWorld"}"#, &mut scratch);
95
    /// ```
96
    pub fn with_buffer(input: &'a str, scratch_buffer: &'b mut [u8]) -> Self {
35✔
97
        Self::with_buffer_from_slice(input.as_bytes(), scratch_buffer)
35✔
98
    }
35✔
99

100
    /// Creates a new parser from a byte slice with a scratch buffer.
101
    ///
102
    /// Use when JSON contains string escapes that need unescaping.
103
    ///
104
    /// # Example
105
    /// ```
106
    /// # use picojson::PullParser;
107
    /// let mut scratch = [0u8; 1024];
108
    /// let parser = PullParser::with_buffer_from_slice(br#"{"msg": "Hello\nWorld"}"#, &mut scratch);
109
    /// ```
110
    pub fn with_buffer_from_slice(input: &'a [u8], scratch_buffer: &'b mut [u8]) -> Self {
36✔
111
        Self::with_config_and_buffer_from_slice(input, scratch_buffer)
36✔
112
    }
36✔
113
}
114

115
/// Generic constructor for PullParser with custom configurations
116
impl<'a, 'b, C: BitStackConfig> PullParser<'a, 'b, C> {
117
    /// Creates a new parser with a custom `BitStackConfig`.
118
    ///
119
    /// This parser assumes no string escapes will be encountered. If escapes are found,
120
    /// parsing will fail. For JSON with escapes, use `with_config_and_buffer`.
121
    pub fn with_config(input: &'a str) -> Self {
1✔
122
        Self::with_config_from_slice(input.as_bytes())
1✔
123
    }
1✔
124

125
    /// Creates a new parser from a byte slice with a custom `BitStackConfig`.
126
    ///
127
    /// Assumes no string escapes will be encountered. For JSON with escapes, use [`with_config_and_buffer_from_slice`].
128
    ///
129
    /// [`with_config_and_buffer_from_slice`]: Self::with_config_and_buffer_from_slice
130
    pub fn with_config_from_slice(input: &'a [u8]) -> Self {
10✔
131
        Self::with_config_and_buffer_from_slice(input, &mut [])
10✔
132
    }
10✔
133

134
    /// Creates a new parser with a custom `BitStackConfig` and a user-provided scratch buffer.
135
    ///
136
    /// Use this when your JSON contains string escapes (like `\n`, `\"`, `\u0041`).
137
    ///
138
    /// # Arguments
139
    /// * `input` - A string slice containing the JSON data to be parsed.
140
    /// * `scratch_buffer` - A mutable byte slice for temporary string unescaping operations.
141
    ///   This buffer needs to be at least as long as the longest
142
    ///   contiguous token (string, key, number) in the input.
143
    pub fn with_config_and_buffer(input: &'a str, scratch_buffer: &'b mut [u8]) -> Self {
2✔
144
        Self::with_config_and_buffer_from_slice(input.as_bytes(), scratch_buffer)
2✔
145
    }
2✔
146

147
    /// Creates a new parser from a byte slice with a custom `BitStackConfig` and scratch buffer.
148
    ///
149
    /// Use when JSON contains string escapes that need unescaping.
150
    /// This is the core constructor that all other constructors delegate to.
151
    pub fn with_config_and_buffer_from_slice(
48✔
152
        input: &'a [u8],
48✔
153
        scratch_buffer: &'b mut [u8],
48✔
154
    ) -> Self {
48✔
155
        let copy_on_escape = CopyOnEscape::new(input, scratch_buffer);
48✔
156
        PullParser {
48✔
157
            tokenizer: Tokenizer::new(),
48✔
158
            buffer: SliceInputBuffer::new(input),
48✔
159
            parser_state: ParserState::new(),
48✔
160
            copy_on_escape,
48✔
161
            unicode_escape_collector: UnicodeEscapeCollector::new(),
48✔
162
        }
48✔
163
    }
48✔
164

165
    fn have_events(&self) -> bool {
2,405✔
166
        self.parser_state.evts.iter().any(|evt| evt.is_some())
4,370✔
167
    }
2,405✔
168

169
    /// Helper function to parse a number from the buffer given a start position.
170
    /// Uses unified number parsing logic with centralized delimiter handling.
171
    fn parse_number_from_buffer(
31✔
172
        &mut self,
31✔
173
        start: usize,
31✔
174
        from_container_end: bool,
31✔
175
    ) -> Result<Event, ParseError> {
31✔
176
        crate::number_parser::parse_number_event(&self.buffer, start, from_container_end)
31✔
177
    }
31✔
178

179
    /// Helper method to handle simple escape tokens using EscapeProcessor
180
    /// Converts EventToken back to original escape character and processes it
181
    fn handle_simple_escape_token(
15✔
182
        &mut self,
15✔
183
        escape_token: &EventToken,
15✔
184
    ) -> Result<Option<Event>, ParseError> {
15✔
185
        // Use unified escape token processing
186
        let unescaped_char = EscapeProcessor::process_escape_token(escape_token)?;
15✔
187

188
        // Handle the escape using existing logic
189
        self.handle_escape_event(unescaped_char)
15✔
190
    }
15✔
191

192
    /// Handles escape sequence events by delegating to CopyOnEscape if we're inside a string or key
193
    fn handle_escape_event(&mut self, escape_char: u8) -> Result<Option<Event>, ParseError> {
15✔
194
        if let State::String(_) | State::Key(_) = self.parser_state.state {
15✔
195
            self.copy_on_escape
15✔
196
                .handle_escape(self.buffer.current_pos(), escape_char)?;
15✔
197
        }
×
198
        Ok(None)
13✔
199
    }
15✔
200

201
    /// Process Unicode escape sequence using shared UnicodeEscapeCollector
202
    /// Extracts hex digits from buffer and processes them through the collector
203
    fn process_unicode_escape_with_collector(&mut self) -> Result<(), ParseError> {
1✔
204
        let current_pos = self.buffer.current_pos();
1✔
205
        let hex_slice_provider = |start, end| self.buffer.slice(start, end).map_err(Into::into);
1✔
206

207
        let mut utf8_buf = [0u8; 4];
1✔
208
        let (utf8_bytes, escape_start_pos) =
1✔
209
            crate::escape_processor::process_unicode_escape_sequence(
1✔
210
                current_pos,
1✔
211
                &mut self.unicode_escape_collector,
1✔
212
                hex_slice_provider,
1✔
213
                &mut utf8_buf,
1✔
214
            )?;
×
215

216
        self.copy_on_escape
1✔
217
            .handle_unicode_escape(escape_start_pos, utf8_bytes)
1✔
218
    }
1✔
219

220
    fn pull_tokenizer_events(&mut self) -> Result<(), ParseError> {
1,946✔
221
        use crate::slice_input_buffer::InputBuffer;
222
        if self.buffer.is_past_end() {
1,946✔
223
            return Err(ParseError::EndOfData);
×
224
        }
1,946✔
225
        let mut callback = |event, _len| {
1,946✔
226
            for evt in self.parser_state.evts.iter_mut() {
499✔
227
                if evt.is_none() {
499✔
228
                    *evt = Some(event);
470✔
229
                    return;
470✔
230
                }
29✔
231
            }
232
        };
470✔
233

234
        let res = match self.buffer.consume_byte() {
1,946✔
235
            Err(crate::slice_input_buffer::Error::ReachedEnd) => {
236
                self.tokenizer.finish(&mut callback)
32✔
237
            }
238
            Err(crate::slice_input_buffer::Error::InvalidSliceBounds) => {
239
                return Err(ParseError::UnexpectedState(
×
240
                    "Invalid slice bounds in consume_byte",
×
241
                ));
×
242
            }
243
            Ok(byte) => self.tokenizer.parse_chunk(&[byte], &mut callback),
1,914✔
244
        };
245

246
        if let Err(_tokenizer_error) = res {
1,946✔
247
            return Err(ParseError::TokenizerError);
7✔
248
        }
1,939✔
249
        Ok(())
1,939✔
250
    }
1,946✔
251

252
    pub fn next(&mut self) -> Option<Result<Event, ParseError>> {
×
253
        match self.next_event() {
×
254
            Ok(Event::EndDocument) => None,
×
255
            other => Some(other),
×
256
        }
257
    }
×
258

259
    /// Returns the next JSON event or an error if parsing fails.
260
    /// Parsing continues until `EndDocument` is returned or an error occurs.
261
    pub fn next_event(&mut self) -> Result<Event, ParseError> {
289✔
262
        if self.buffer.is_past_end() {
289✔
263
            return Ok(Event::EndDocument);
2✔
264
        }
287✔
265
        loop {
266
            while !self.have_events() {
2,405✔
267
                self.pull_tokenizer_events()?;
1,946✔
268
                if self.buffer.is_past_end() {
1,939✔
269
                    return Ok(Event::EndDocument);
29✔
270
                }
1,910✔
271
            }
272
            // Find and move out the first available event to avoid holding mutable borrow during processing
273
            let taken_event = {
459✔
274
                let mut found_event = None;
459✔
275
                for evt in self.parser_state.evts.iter_mut() {
478✔
276
                    if evt.is_some() {
478✔
277
                        found_event = evt.take();
459✔
278
                        break;
459✔
279
                    }
19✔
280
                }
281
                found_event
459✔
282
            };
283

284
            if let Some(taken) = taken_event {
459✔
285
                let res = match taken {
457✔
286
                    // Container events
287
                    ujson::Event::ObjectStart => EventResult::Complete(Event::StartObject),
42✔
288
                    ujson::Event::ObjectEnd => EventResult::Complete(Event::EndObject),
30✔
289
                    ujson::Event::ArrayStart => EventResult::Complete(Event::StartArray),
13✔
290
                    ujson::Event::ArrayEnd => EventResult::Complete(Event::EndArray),
9✔
291

292
                    // String/Key events
293
                    ujson::Event::Begin(EventToken::Key) => {
294
                        self.parser_state.state = State::Key(self.buffer.current_pos());
69✔
295
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
69✔
296
                        EventResult::Continue
69✔
297
                    }
298
                    ujson::Event::End(EventToken::Key) => EventResult::ExtractKey,
69✔
299
                    ujson::Event::Begin(EventToken::String) => {
300
                        self.parser_state.state = State::String(self.buffer.current_pos());
46✔
301
                        self.copy_on_escape.begin_string(self.buffer.current_pos());
46✔
302
                        EventResult::Continue
46✔
303
                    }
304
                    ujson::Event::End(EventToken::String) => EventResult::ExtractString,
41✔
305

306
                    // Number events
307
                    ujson::Event::Begin(
308
                        EventToken::Number
309
                        | EventToken::NumberAndArray
310
                        | EventToken::NumberAndObject,
311
                    ) => {
312
                        let number_start =
33✔
313
                            ContentRange::number_start_from_current(self.buffer.current_pos());
33✔
314
                        self.parser_state.state = State::Number(number_start);
33✔
315
                        EventResult::Continue
33✔
316
                    }
317
                    ujson::Event::End(EventToken::Number) => EventResult::ExtractNumber(false),
17✔
318
                    ujson::Event::End(EventToken::NumberAndArray) => {
319
                        EventResult::ExtractNumber(true)
7✔
320
                    }
321
                    ujson::Event::End(EventToken::NumberAndObject) => {
322
                        EventResult::ExtractNumber(true)
7✔
323
                    }
324
                    // Boolean and null values
325
                    ujson::Event::Begin(
326
                        EventToken::True | EventToken::False | EventToken::Null,
327
                    ) => EventResult::Continue,
14✔
328
                    ujson::Event::End(EventToken::True) => EventResult::Complete(Event::Bool(true)),
6✔
329
                    ujson::Event::End(EventToken::False) => {
330
                        EventResult::Complete(Event::Bool(false))
4✔
331
                    }
332
                    ujson::Event::End(EventToken::Null) => EventResult::Complete(Event::Null),
4✔
333
                    // Escape sequence handling
334
                    ujson::Event::Begin(
335
                        escape_token @ (EventToken::EscapeQuote
15✔
336
                        | EventToken::EscapeBackslash
337
                        | EventToken::EscapeSlash
338
                        | EventToken::EscapeBackspace
339
                        | EventToken::EscapeFormFeed
340
                        | EventToken::EscapeNewline
341
                        | EventToken::EscapeCarriageReturn
342
                        | EventToken::EscapeTab),
343
                    ) => {
344
                        // Use EscapeProcessor for all simple escape sequences
345
                        self.handle_simple_escape_token(&escape_token)?;
15✔
346
                        EventResult::Continue
13✔
347
                    }
348
                    ujson::Event::Begin(EventToken::UnicodeEscape) => {
349
                        // Start Unicode escape collection - reset collector for new sequence
350
                        // Only handle if we're inside a string or key
351
                        match self.parser_state.state {
1✔
352
                            State::String(_) | State::Key(_) => {
1✔
353
                                self.unicode_escape_collector.reset();
1✔
354
                            }
1✔
355
                            _ => {} // Ignore if not in string/key
×
356
                        }
357
                        EventResult::Continue
1✔
358
                    }
359
                    ujson::Event::End(EventToken::UnicodeEscape) => {
360
                        // Handle end of Unicode escape sequence (\uXXXX) using shared collector
361
                        match self.parser_state.state {
1✔
362
                            State::String(_) | State::Key(_) => {
363
                                // Process Unicode escape using shared collector logic
364
                                self.process_unicode_escape_with_collector()?;
1✔
365
                            }
366
                            _ => {} // Ignore if not in string/key context
×
367
                        }
368
                        EventResult::Continue
1✔
369
                    }
370
                    // EscapeSequence events (only emitted when flag is enabled, ignored in original parser)
371
                    ujson::Event::Begin(EventToken::EscapeSequence) => {
372
                        // Ignore in original parser since it uses slice-based parsing
373
                        EventResult::Continue
18✔
374
                    }
375
                    ujson::Event::End(EventToken::EscapeSequence) => {
376
                        // Ignore in original parser since it uses slice-based parsing
377
                        EventResult::Continue
×
378
                    }
379
                    ujson::Event::End(
380
                        EventToken::EscapeQuote
381
                        | EventToken::EscapeBackslash
382
                        | EventToken::EscapeSlash
383
                        | EventToken::EscapeBackspace
384
                        | EventToken::EscapeFormFeed
385
                        | EventToken::EscapeNewline
386
                        | EventToken::EscapeCarriageReturn
387
                        | EventToken::EscapeTab,
388
                    ) => {
389
                        // End of escape sequence - ignored here
390
                        EventResult::Continue
13✔
391
                    }
392
                    #[cfg(test)]
393
                    ujson::Event::Uninitialized => {
394
                        return Err(ParseError::UnexpectedState("Uninitialized event"));
×
395
                    }
396
                };
397
                match res {
457✔
398
                    EventResult::Complete(event) => break Ok(event),
108✔
399
                    EventResult::Continue => continue,
208✔
400
                    EventResult::ExtractKey => {
401
                        if let State::Key(_start) = self.parser_state.state {
69✔
402
                            self.parser_state.state = State::None;
69✔
403
                            // Use CopyOnEscape to get the final key result
404
                            let end_pos = ContentRange::end_position_excluding_delimiter(
69✔
405
                                self.buffer.current_pos(),
69✔
406
                            );
407
                            let key_result = self.copy_on_escape.end_string(end_pos)?;
69✔
408
                            break Ok(Event::Key(key_result));
69✔
409
                        } else {
410
                            break Err(ParserErrorHandler::state_mismatch("key", "end"));
×
411
                        }
412
                    }
413
                    EventResult::ExtractString => {
414
                        if let State::String(_value) = self.parser_state.state {
41✔
415
                            self.parser_state.state = State::None;
41✔
416
                            // Use CopyOnEscape to get the final string result
417
                            let end_pos = ContentRange::end_position_excluding_delimiter(
41✔
418
                                self.buffer.current_pos(),
41✔
419
                            );
420
                            let value_result = self.copy_on_escape.end_string(end_pos)?;
41✔
421
                            break Ok(Event::String(value_result));
41✔
422
                        } else {
423
                            break Err(ParserErrorHandler::state_mismatch("string", "end"));
×
424
                        }
425
                    }
426
                    EventResult::ExtractNumber(from_container_end) => {
31✔
427
                        if let State::Number(start) = self.parser_state.state {
31✔
428
                            // Reset state before parsing to stop selective copying
429
                            self.parser_state.state = State::None;
31✔
430
                            let event = self.parse_number_from_buffer(start, from_container_end)?;
31✔
431
                            break Ok(event);
31✔
432
                        } else {
433
                            break Err(ParseError::UnexpectedState(
×
434
                                "Number end without Number start",
×
435
                            ));
×
436
                        }
437
                    }
438
                }
439
            } else {
440
                // No event available - this shouldn't happen since we ensured have_events() above
441
                break Err(ParseError::UnexpectedState(
×
NEW
442
                    "No events available after ensuring events exist",
×
443
                ));
×
444
            }
445
        }
446
    }
289✔
447
}
448

449
#[cfg(test)]
450
mod tests {
451
    use super::*;
452
    use crate::{ArrayBitStack, BitStackStruct, String};
453
    use test_log::test;
454

455
    #[test]
456
    fn make_parser() {
457
        let input = r#"{"key": "value"}"#;
458
        let mut scratch = [0u8; 1024];
459
        let mut parser = PullParser::with_buffer(input, &mut scratch);
460
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
461
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
462
        assert_eq!(
463
            parser.next_event(),
464
            Ok(Event::String(String::Borrowed("value")))
465
        );
466
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
467
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
468
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
469
    }
470

471
    #[test]
472
    fn parse_number() {
473
        let input = r#"{"key": 1242}"#;
474
        let mut scratch = [0u8; 1024];
475
        let mut parser = PullParser::with_buffer(input, &mut scratch);
476
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
477
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
478
        // Check number value using new JsonNumber API
479
        match parser.next_event() {
480
            Ok(Event::Number(num)) => {
481
                assert_eq!(num.as_str(), "1242");
482
                assert_eq!(num.as_int(), Some(1242));
483
            }
484
            other => panic!("Expected Number, got: {:?}", other),
485
        }
486
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
487
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
488
    }
489

490
    #[test]
491
    fn parse_bool_and_null() {
492
        let input = r#"{"key": true, "key2": false, "key3": null}"#;
493
        let mut scratch = [0u8; 1024];
494
        let mut parser = PullParser::with_buffer(input, &mut scratch);
495
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
496
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
497
        assert_eq!(parser.next_event(), Ok(Event::Bool(true)));
498
        assert_eq!(
499
            parser.next_event(),
500
            Ok(Event::Key(String::Borrowed("key2")))
501
        );
502
        assert_eq!(parser.next_event(), Ok(Event::Bool(false)));
503
        assert_eq!(
504
            parser.next_event(),
505
            Ok(Event::Key(String::Borrowed("key3")))
506
        );
507
        assert_eq!(parser.next_event(), Ok(Event::Null));
508
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
509
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
510
    }
511

512
    #[test]
513
    fn parse_array() {
514
        #[cfg(feature = "float-error")]
515
        let input = r#"{"key": [1, 2, 3]}"#; // No floats for float-error config
516
        #[cfg(not(feature = "float-error"))]
517
        let input = r#"{"key": [1, 2.2, 3]}"#; // Include float for other configs
518

519
        let mut scratch = [0u8; 1024];
520
        let mut parser = PullParser::with_buffer(input, &mut scratch);
521
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
522
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
523
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
524

525
        // First number: 1 (integer)
526
        match parser.next_event() {
527
            Ok(Event::Number(num)) => {
528
                assert_eq!(num.as_str(), "1");
529
                assert_eq!(num.as_int(), Some(1));
530
            }
531
            other => panic!("Expected Number(1), got: {:?}", other),
532
        }
533

534
        // Second number: depends on configuration
535
        match parser.next_event() {
536
            Ok(Event::Number(num)) => {
537
                #[cfg(feature = "float-error")]
538
                {
539
                    assert_eq!(num.as_str(), "2");
540
                    assert_eq!(num.as_int(), Some(2));
541
                }
542
                #[cfg(not(feature = "float-error"))]
543
                {
544
                    assert_eq!(num.as_str(), "2.2");
545
                    #[cfg(feature = "float")]
546
                    assert_eq!(num.as_f64(), Some(2.2));
547
                    #[cfg(not(feature = "float-error"))]
548
                    assert!(num.is_float());
549
                }
550
            }
551
            other => panic!("Expected Number, got: {:?}", other),
552
        }
553

554
        // Third number: 3 (integer)
555
        match parser.next_event() {
556
            Ok(Event::Number(num)) => {
557
                assert_eq!(num.as_str(), "3");
558
                assert_eq!(num.as_int(), Some(3));
559
            }
560
            other => panic!("Expected Number(3), got: {:?}", other),
561
        }
562

563
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
564
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
565
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
566
    }
567

568
    #[test]
569
    fn test_simple_parser_api() {
570
        let input = r#"{"name": "test"}"#;
571
        let mut scratch = [0u8; 1024];
572
        let mut parser = PullParser::with_buffer(input, &mut scratch);
573

574
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
575
        assert_eq!(
576
            parser.next_event(),
577
            Ok(Event::Key(String::Borrowed("name")))
578
        );
579
        assert_eq!(
580
            parser.next_event(),
581
            Ok(Event::String(String::Borrowed("test")))
582
        );
583
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
584
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
585
    }
586

587
    #[test]
588
    fn test_parser_with_escaped_strings() {
589
        // Use regular string literal to properly include escape sequences
590
        let input = "{\"name\": \"John\\nDoe\", \"message\": \"Hello\\tWorld!\"}";
591
        let mut scratch = [0u8; 1024];
592
        let mut parser = PullParser::with_buffer(input, &mut scratch);
593

594
        // Test that the parser correctly handles escaped strings
595
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
596

597
        // Key should be simple (no escapes) -> Borrowed
598
        if let Ok(Event::Key(key)) = parser.next_event() {
599
            assert_eq!(&*key, "name");
600
            // This should be the fast path (borrowed)
601
            assert!(matches!(key, String::Borrowed(_)));
602
        } else {
603
            panic!("Expected Key event");
604
        }
605

606
        // Value should have escapes -> Unescaped
607
        if let Ok(Event::String(value)) = parser.next_event() {
608
            assert_eq!(&*value, "John\nDoe");
609
            // This should be the slow path (unescaped)
610
            assert!(matches!(value, String::Unescaped(_)));
611
        } else {
612
            panic!("Expected String event");
613
        }
614

615
        // Second key should be simple -> Borrowed
616
        if let Ok(Event::Key(key)) = parser.next_event() {
617
            assert_eq!(&*key, "message");
618
            assert!(matches!(key, String::Borrowed(_)));
619
        } else {
620
            panic!("Expected Key event");
621
        }
622

623
        // Second value should have escapes -> Unescaped
624
        if let Ok(Event::String(value)) = parser.next_event() {
625
            assert_eq!(&*value, "Hello\tWorld!");
626
            assert!(matches!(value, String::Unescaped(_)));
627
        } else {
628
            panic!("Expected String event");
629
        }
630

631
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
632
    }
633

634
    #[test]
635
    fn test_copy_on_escape_optimization() {
636
        // Use regular string literal to include proper escape sequences
637
        let input = "{\"simple\": \"no escapes\", \"complex\": \"has\\nescapes\"}";
638
        let mut scratch = [0u8; 1024];
639
        let mut parser = PullParser::with_buffer(input, &mut scratch);
640

641
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
642

643
        // "simple" key should be borrowed (fast path)
644
        if let Ok(Event::Key(key)) = parser.next_event() {
645
            assert_eq!(&*key, "simple");
646
            assert!(matches!(key, String::Borrowed(_)));
647
        } else {
648
            panic!("Expected Key event");
649
        }
650

651
        // "no escapes" value should be borrowed (fast path)
652
        if let Ok(Event::String(value)) = parser.next_event() {
653
            assert_eq!(&*value, "no escapes");
654
            assert!(matches!(value, String::Borrowed(_)));
655
        } else {
656
            panic!("Expected String event");
657
        }
658

659
        // "complex" key should be borrowed (fast path)
660
        if let Ok(Event::Key(key)) = parser.next_event() {
661
            assert_eq!(&*key, "complex");
662
            assert!(matches!(key, String::Borrowed(_)));
663
        } else {
664
            panic!("Expected Key event");
665
        }
666

667
        // "has\\nescapes" value should be unescaped (slow path)
668
        if let Ok(Event::String(value)) = parser.next_event() {
669
            assert_eq!(&*value, "has\nescapes");
670
            assert!(matches!(value, String::Unescaped(_)));
671
        } else {
672
            panic!("Expected String event");
673
        }
674

675
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
676
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
677
    }
678

679
    #[test]
680
    fn test_coe2_integration_multiple_escapes() {
681
        let input = r#"{"key": "a\nb\tc\rd"}"#;
682
        let mut scratch = [0u8; 1024];
683
        let mut parser = PullParser::with_buffer(input, &mut scratch);
684

685
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
686
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
687

688
        let string_event = parser.next_event().unwrap();
689
        match string_event {
690
            Event::String(String::Unescaped(s)) => {
691
                assert_eq!(s, "a\nb\tc\rd");
692
            }
693
            _ => panic!("Expected unescaped string value, got: {:?}", string_event),
694
        }
695
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
696
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
697
    }
698

699
    #[test]
700
    fn test_coe2_integration_zero_copy_path() {
701
        let input = r#"{"simple": "no_escapes_here"}"#;
702
        let mut scratch = [0u8; 1024];
703
        let mut parser = PullParser::with_buffer(input, &mut scratch);
704

705
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
706
        assert_eq!(
707
            parser.next_event(),
708
            Ok(Event::Key(String::Borrowed("simple")))
709
        );
710

711
        // This should be borrowed (zero-copy) since no escapes
712
        let string_event = parser.next_event().unwrap();
713
        match string_event {
714
            Event::String(String::Borrowed(s)) => {
715
                assert_eq!(s, "no_escapes_here");
716
            }
717
            _ => panic!(
718
                "Expected borrowed string value for zero-copy, got: {:?}",
719
                string_event
720
            ),
721
        }
722
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
723
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
724
    }
725

726
    #[test]
727
    fn test_coe2_integration_mixed_strings() {
728
        let input = r#"["plain", "with\nescapes", "plain2", "more\tescapes"]"#;
729
        let mut scratch = [0u8; 1024];
730
        let mut parser = PullParser::with_buffer(input, &mut scratch);
731

732
        assert_eq!(parser.next_event(), Ok(Event::StartArray));
733

734
        // First string: no escapes -> borrowed
735
        match parser.next_event().unwrap() {
736
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain"),
737
            other => panic!("Expected borrowed string, got: {:?}", other),
738
        }
739

740
        // Second string: has escapes -> unescaped
741
        match parser.next_event().unwrap() {
742
            Event::String(String::Unescaped(s)) => assert_eq!(s, "with\nescapes"),
743
            other => panic!("Expected unescaped string, got: {:?}", other),
744
        }
745

746
        // Third string: no escapes -> borrowed
747
        match parser.next_event().unwrap() {
748
            Event::String(String::Borrowed(s)) => assert_eq!(s, "plain2"),
749
            other => panic!("Expected borrowed string, got: {:?}", other),
750
        }
751

752
        // Fourth string: has escapes -> unescaped
753
        match parser.next_event().unwrap() {
754
            Event::String(String::Unescaped(s)) => assert_eq!(s, "more\tescapes"),
755
            other => panic!("Expected unescaped string, got: {:?}", other),
756
        }
757

758
        assert_eq!(parser.next_event(), Ok(Event::EndArray));
759
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
760
    }
761

762
    #[test]
763
    fn test_unicode_escape_integration() {
764
        let input = r#"{"key": "Hello\u0041World"}"#; // \u0041 = 'A'
765
        let mut scratch = [0u8; 1024];
766
        let mut parser = PullParser::with_buffer(input, &mut scratch);
767

768
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
769
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
770

771
        // The string with Unicode escape should be unescaped
772
        match parser.next_event().unwrap() {
773
            Event::String(String::Unescaped(s)) => {
774
                assert_eq!(s, "HelloAWorld");
775
            }
776
            other => panic!("Expected unescaped string value, got: {:?}", other),
777
        }
778

779
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
780
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
781
    }
782

783
    #[test_log::test]
784
    fn test_original_parser_escape_trace() {
785
        // Test escape sequence processing with logging
786
        let input = r#""a\nb""#;
787
        let mut scratch = [0u8; 1024];
788
        let mut parser = PullParser::with_buffer(input, &mut scratch);
789

790
        // Should get String with unescaped content
791
        let event = parser.next_event().unwrap();
792
        if let Event::String(s) = event {
793
            assert_eq!(&*s, "a\nb");
794
        } else {
795
            panic!("Expected String event, got {:?}", event);
796
        }
797

798
        // Should get EndDocument
799
        let event = parser.next_event().unwrap();
800
        assert_eq!(event, Event::EndDocument);
801
    }
802

803
    #[test]
804
    fn make_parser_from_slice() {
805
        let input = br#"{"key": "value"}"#;
806
        let mut scratch = [0u8; 1024];
807
        let mut parser = PullParser::with_buffer_from_slice(input, &mut scratch);
808
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
809
        assert_eq!(parser.next_event(), Ok(Event::Key(String::Borrowed("key"))));
810
        assert_eq!(
811
            parser.next_event(),
812
            Ok(Event::String(String::Borrowed("value")))
813
        );
814
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
815
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
816
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
817
    }
818

819
    #[test]
820
    fn test_with_config_constructors() {
821
        // Test with_config constructor (no escapes)
822
        let json = r#"{"simple": "no_escapes"}"#;
823
        let mut parser = PullParser::<BitStackStruct<u64, u16>>::with_config(json);
824

825
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
826
        assert_eq!(
827
            parser.next_event(),
828
            Ok(Event::Key(String::Borrowed("simple")))
829
        );
830
        assert_eq!(
831
            parser.next_event(),
832
            Ok(Event::String(String::Borrowed("no_escapes")))
833
        );
834
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
835
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
836
    }
837

838
    #[test]
839
    fn test_with_config_and_buffer_constructors() {
840
        // Test with_config_and_buffer constructor (with escapes)
841
        let json = r#"{"escaped": "hello\nworld"}"#;
842
        let mut scratch = [0u8; 64];
843
        let mut parser =
844
            PullParser::<BitStackStruct<u64, u16>>::with_config_and_buffer(json, &mut scratch);
845

846
        assert_eq!(parser.next_event(), Ok(Event::StartObject));
847
        assert_eq!(
848
            parser.next_event(),
849
            Ok(Event::Key(String::Borrowed("escaped")))
850
        );
851

852
        if let Ok(Event::String(s)) = parser.next_event() {
853
            assert_eq!(s.as_ref(), "hello\nworld"); // Escape should be processed
854
        } else {
855
            panic!("Expected String event");
856
        }
857

858
        assert_eq!(parser.next_event(), Ok(Event::EndObject));
859
        assert_eq!(parser.next_event(), Ok(Event::EndDocument));
860
    }
861

862
    #[test]
863
    fn test_alternative_config_deep_nesting() {
864
        // Test that custom BitStack configs can handle deeper nesting
865
        let json = r#"{"a":{"b":{"c":{"d":{"e":"deep"}}}}}"#;
866
        let mut scratch = [0u8; 64];
867
        let mut parser =
868
            PullParser::<ArrayBitStack<8, u32, u16>>::with_config_and_buffer(json, &mut scratch);
869

870
        // Parse the deep structure
871
        let mut depth = 0;
872
        while let Ok(event) = parser.next_event() {
873
            match event {
874
                Event::StartObject => depth += 1,
875
                Event::EndObject => depth -= 1,
876
                Event::EndDocument => break,
877
                _ => {}
878
            }
879
        }
880

881
        // Should have successfully parsed a 5-level deep structure
882
        assert_eq!(depth, 0); // All objects should be closed
883
    }
884
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc