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

kaidokert / picojson-rs / 16296752393

15 Jul 2025 02:54PM UTC coverage: 91.544% (-2.2%) from 93.785%
16296752393

Pull #57

github

web-flow
Merge cdc45a9eb into 6be34a4b2
Pull Request #57: Even bigger refactor

481 of 649 new or added lines in 10 files covered. (74.11%)

17 existing lines in 5 files now uncovered.

4742 of 5180 relevant lines covered (91.54%)

739.86 hits per line

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

85.31
/picojson/src/event_processor.rs
1
// SPDX-License-Identifier: Apache-2.0
2

3
//! Shared event processing logic between SliceParser and StreamParser.
4
//!
5
//! This module extracts the common event handling patterns to reduce code duplication
6
//! while preserving the performance characteristics of each parser type.
7

8
use crate::ujson::EventToken;
9
use crate::{Event, ParseError};
10

11
/// Escape handling trait for abstracting escape sequence processing between parsers
12
pub trait EscapeHandler {
13
    /// Get the current parser state for escape context checking
14
    fn parser_state(&self) -> &crate::shared::State;
15

16
    /// Process Unicode escape sequence using shared collector logic
17
    fn process_unicode_escape_with_collector(&mut self) -> Result<(), crate::ParseError>;
18

19
    /// Handle a simple escape character (after EscapeProcessor conversion)
20
    fn handle_simple_escape_char(&mut self, escape_char: u8) -> Result<(), crate::ParseError>;
21

22
    /// Begin escape sequence processing (lifecycle method with default no-op implementation)
23
    /// Called when escape sequence processing begins (e.g., on Begin(EscapeSequence))
UNCOV
24
    fn begin_escape_sequence(&mut self) -> Result<(), crate::ParseError> {
×
UNCOV
25
        Ok(())
×
UNCOV
26
    }
×
27

28
    /// Begin unicode escape sequence processing
29
    /// Default implementation is no-op - suitable for parsers that don't need special handling
30
    fn begin_unicode_escape(&mut self) -> Result<(), crate::ParseError> {
35✔
31
        Ok(())
35✔
32
    }
35✔
33
}
34

35
/// Result of processing a tokenizer event
36
#[derive(Debug)]
37
pub enum EventResult<'a, 'b> {
38
    /// Event processing is complete, return this event to the user
39
    Complete(Event<'a, 'b>),
40
    /// Continue processing more tokenizer events
41
    Continue,
42
    /// Extract string content (delegate to parser-specific logic)
43
    ExtractString,
44
    /// Extract key content (delegate to parser-specific logic)
45
    ExtractKey,
46
    /// Extract number content (delegate to parser-specific logic)
47
    /// bool indicates if number was terminated by container delimiter
48
    ExtractNumber(bool),
49
}
50

51
/// Process Begin events that have similar patterns between parsers
52
pub fn process_begin_events<C: ContentExtractor>(
4,382✔
53
    event: &crate::ujson::Event,
4,382✔
54
    content_extractor: &mut C,
4,382✔
55
) -> Option<EventResult<'static, 'static>> {
4,382✔
56
    use crate::shared::{ContentRange, State};
57

58
    match event {
3,490✔
59
        // String/Key Begin events - nearly identical patterns
60
        crate::ujson::Event::Begin(EventToken::Key) => {
61
            let pos = content_extractor.current_position();
356✔
62
            *content_extractor.parser_state_mut() = State::Key(pos);
356✔
63
            content_extractor.begin_string_content(pos);
356✔
64
            Some(EventResult::Continue)
356✔
65
        }
66
        crate::ujson::Event::Begin(EventToken::String) => {
67
            let pos = content_extractor.current_position();
769✔
68
            *content_extractor.parser_state_mut() = State::String(pos);
769✔
69
            content_extractor.begin_string_content(pos);
769✔
70
            Some(EventResult::Continue)
769✔
71
        }
72

73
        // Number Begin events - identical logic
74
        crate::ujson::Event::Begin(
75
            EventToken::Number | EventToken::NumberAndArray | EventToken::NumberAndObject,
76
        ) => {
77
            let pos = content_extractor.current_position();
471✔
78
            let number_start = ContentRange::number_start_from_current(pos);
471✔
79
            *content_extractor.parser_state_mut() = State::Number(number_start);
471✔
80
            Some(EventResult::Continue)
471✔
81
        }
82

83
        // Primitive Begin events - identical logic
84
        crate::ujson::Event::Begin(EventToken::True | EventToken::False | EventToken::Null) => {
85
            Some(EventResult::Continue)
25✔
86
        }
87

88
        _ => None,
2,761✔
89
    }
90
}
4,382✔
91

92
/// Clear event storage array - utility function
93
pub fn clear_events(event_storage: &mut [Option<crate::ujson::Event>; 2]) {
20,972✔
94
    event_storage[0] = None;
20,972✔
95
    event_storage[1] = None;
20,972✔
96
}
20,972✔
97

98
/// Trait for content extraction operations that differ between parsers
99
/// Consolidates ParserContext and ContentExtractor functionality
100
pub trait ContentExtractor: EscapeHandler {
101
    /// Get current position in the input
102
    fn current_position(&self) -> usize;
103

104
    /// Begin string/key content processing at current position
105
    fn begin_string_content(&mut self, pos: usize);
106

107
    /// Get mutable access to parser state
108
    fn parser_state_mut(&mut self) -> &mut crate::shared::State;
109

110
    /// Get mutable access to the Unicode escape collector
111
    /// This eliminates the need for wrapper methods that just forward calls
112
    fn unicode_escape_collector_mut(
113
        &mut self,
114
    ) -> &mut crate::escape_processor::UnicodeEscapeCollector;
115

116
    /// Extract string content using parser-specific logic
117
    fn extract_string_content(
118
        &mut self,
119
        start_pos: usize,
120
    ) -> Result<crate::Event<'_, '_>, crate::ParseError>;
121

122
    /// Extract key content using parser-specific logic
123
    fn extract_key_content(
124
        &mut self,
125
        start_pos: usize,
126
    ) -> Result<crate::Event<'_, '_>, crate::ParseError>;
127

128
    /// Extract number content using parser-specific logic
129
    fn extract_number_content(
130
        &mut self,
131
        start_pos: usize,
132
        from_container_end: bool,
133
    ) -> Result<crate::Event<'_, '_>, crate::ParseError>;
134

135
    /// Extract a completed number using shared number parsing logic
136
    ///
137
    /// # Arguments
138
    /// * `start_pos` - Position where the number started
139
    /// * `from_container_end` - True if number was terminated by container delimiter
140
    /// * `finished` - True if the parser has finished processing input (StreamParser-specific)
141
    fn extract_number(
142
        &mut self,
143
        start_pos: usize,
144
        from_container_end: bool,
145
        finished: bool,
146
    ) -> Result<crate::Event<'_, '_>, crate::ParseError>;
147

148
    /// Shared validation and extraction for string content
149
    fn validate_and_extract_string(&mut self) -> Result<crate::Event<'_, '_>, crate::ParseError> {
432✔
150
        let start_pos = match *self.parser_state() {
432✔
151
            crate::shared::State::String(pos) => pos,
432✔
152
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
153
        };
154

155
        // Check for incomplete surrogate pairs before ending the string
156
        if self
432✔
157
            .unicode_escape_collector_mut()
432✔
158
            .has_pending_high_surrogate()
432✔
159
        {
160
            return Err(crate::ParseError::InvalidUnicodeCodepoint);
3✔
161
        }
429✔
162

163
        *self.parser_state_mut() = crate::shared::State::None;
429✔
164
        self.extract_string_content(start_pos)
429✔
165
    }
432✔
166

167
    /// Shared validation and extraction for key content
168
    fn validate_and_extract_key(&mut self) -> Result<crate::Event<'_, '_>, crate::ParseError> {
325✔
169
        let start_pos = match *self.parser_state() {
325✔
170
            crate::shared::State::Key(pos) => pos,
325✔
171
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
172
        };
173

174
        // Check for incomplete surrogate pairs before ending the key
175
        if self
325✔
176
            .unicode_escape_collector_mut()
325✔
177
            .has_pending_high_surrogate()
325✔
178
        {
179
            return Err(crate::ParseError::InvalidUnicodeCodepoint);
×
180
        }
325✔
181

182
        *self.parser_state_mut() = crate::shared::State::None;
325✔
183
        self.extract_key_content(start_pos)
325✔
184
    }
325✔
185

186
    /// Shared validation and extraction for number content
187
    fn validate_and_extract_number(
27✔
188
        &mut self,
27✔
189
        from_container_end: bool,
27✔
190
    ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
27✔
191
        let start_pos = match *self.parser_state() {
27✔
192
            crate::shared::State::Number(pos) => pos,
27✔
193
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
194
        };
195

196
        *self.parser_state_mut() = crate::shared::State::None;
27✔
197
        self.extract_number_content(start_pos, from_container_end)
27✔
198
    }
27✔
199
}
200

201
/// Creates a standard tokenizer callback for event storage
202
///
203
/// This callback stores tokenizer events in the parser's event array, filling the first
204
/// available slot. This pattern is identical across both SliceParser and StreamParser.
205
pub fn create_tokenizer_callback(
20,975✔
206
    event_storage: &mut [Option<crate::ujson::Event>; 2],
20,975✔
207
) -> impl FnMut(crate::ujson::Event, usize) + '_ {
20,975✔
208
    |event, _len| {
8,578✔
209
        for evt in event_storage.iter_mut() {
9,494✔
210
            if evt.is_none() {
9,494✔
211
                *evt = Some(event);
8,577✔
212
                return;
8,577✔
213
            }
917✔
214
        }
215
    }
8,578✔
216
}
20,975✔
217

218
/// Shared utility to check if any events are waiting to be processed
219
pub fn have_events(event_storage: &[Option<crate::ujson::Event>; 2]) -> bool {
51,032✔
220
    event_storage.iter().any(|evt| evt.is_some())
86,741✔
221
}
51,032✔
222

223
/// Shared utility to extract the first available event from storage
224
pub fn take_first_event(
8,568✔
225
    event_storage: &mut [Option<crate::ujson::Event>; 2],
8,568✔
226
) -> Option<crate::ujson::Event> {
8,568✔
227
    event_storage.iter_mut().find_map(|e| e.take())
9,474✔
228
}
8,568✔
229

230
/// Process Begin(EscapeSequence) events using the enhanced lifecycle interface
231
pub fn process_begin_escape_sequence_event<H: EscapeHandler>(
956✔
232
    handler: &mut H,
956✔
233
) -> Result<(), crate::ParseError> {
956✔
234
    // Only process if we're inside a string or key
235
    match handler.parser_state() {
956✔
236
        crate::shared::State::String(_) | crate::shared::State::Key(_) => {
237
            handler.begin_escape_sequence()?;
956✔
238
        }
239
        _ => {} // Ignore if not in string/key context
×
240
    }
241
    Ok(())
956✔
242
}
956✔
243

244
/// Process simple escape sequence events that have similar patterns between parsers
245
pub fn process_simple_escape_event<C: ContentExtractor>(
692✔
246
    escape_token: &EventToken,
692✔
247
    content_extractor: &mut C,
692✔
248
) -> Result<(), crate::ParseError> {
692✔
249
    // Clear any pending high surrogate state when we encounter a simple escape
250
    // This ensures that interrupted surrogate pairs (like \uD801\n\uDC37) are properly rejected
251
    content_extractor.unicode_escape_collector_mut().reset_all();
692✔
252

253
    // Use unified escape token processing from EscapeProcessor
254
    let unescaped_char =
692✔
255
        crate::escape_processor::EscapeProcessor::process_escape_token(escape_token)?;
692✔
256

257
    // Only process if we're inside a string or key
258
    match content_extractor.parser_state() {
692✔
259
        crate::shared::State::String(_) | crate::shared::State::Key(_) => {
260
            content_extractor.handle_simple_escape_char(unescaped_char)?;
692✔
261
        }
262
        _ => {} // Ignore if not in string/key context
×
263
    }
264

265
    Ok(())
690✔
266
}
692✔
267

268
/// Process Unicode escape begin/end events that have similar patterns between parsers
269
pub fn process_unicode_escape_events<C: ContentExtractor>(
1,804✔
270
    event: &crate::ujson::Event,
1,804✔
271
    content_extractor: &mut C,
1,804✔
272
) -> Result<bool, crate::ParseError> {
1,804✔
273
    match event {
912✔
274
        crate::ujson::Event::Begin(EventToken::UnicodeEscape) => {
275
            // Start Unicode escape collection - reset collector for new sequence
276
            // Only handle if we're inside a string or key
277
            match content_extractor.parser_state() {
220✔
278
                crate::shared::State::String(_) | crate::shared::State::Key(_) => {
279
                    content_extractor.unicode_escape_collector_mut().reset();
220✔
280
                    content_extractor.begin_unicode_escape()?;
220✔
281
                }
282
                _ => {} // Ignore if not in string/key context
×
283
            }
284
            Ok(true) // Event was handled
220✔
285
        }
286
        crate::ujson::Event::End(EventToken::UnicodeEscape) => {
287
            // Handle end of Unicode escape sequence (\uXXXX)
288
            match content_extractor.parser_state() {
202✔
289
                crate::shared::State::String(_) | crate::shared::State::Key(_) => {
290
                    content_extractor.process_unicode_escape_with_collector()?;
202✔
291
                }
292
                _ => {} // Ignore if not in string/key context
×
293
            }
294
            Ok(true) // Event was handled
178✔
295
        }
296
        _ => Ok(false), // Event was not handled
1,382✔
297
    }
298
}
1,804✔
299

300
/// Process simple container and primitive events that are identical between parsers
301
pub fn process_simple_events(event: &crate::ujson::Event) -> Option<EventResult<'static, 'static>> {
8,574✔
302
    match event {
1,953✔
303
        // Container events - identical processing
304
        crate::ujson::Event::ObjectStart => Some(EventResult::Complete(Event::StartObject)),
251✔
305
        crate::ujson::Event::ObjectEnd => Some(EventResult::Complete(Event::EndObject)),
206✔
306
        crate::ujson::Event::ArrayStart => Some(EventResult::Complete(Event::StartArray)),
1,671✔
307
        crate::ujson::Event::ArrayEnd => Some(EventResult::Complete(Event::EndArray)),
1,008✔
308

309
        // Primitive values - identical processing
310
        crate::ujson::Event::End(EventToken::True) => {
311
            Some(EventResult::Complete(Event::Bool(true)))
11✔
312
        }
313
        crate::ujson::Event::End(EventToken::False) => {
314
            Some(EventResult::Complete(Event::Bool(false)))
6✔
315
        }
316
        crate::ujson::Event::End(EventToken::Null) => Some(EventResult::Complete(Event::Null)),
7✔
317

318
        // Content extraction triggers - identical logic
319
        crate::ujson::Event::End(EventToken::String) => Some(EventResult::ExtractString),
433✔
320
        crate::ujson::Event::End(EventToken::Key) => Some(EventResult::ExtractKey),
325✔
321
        crate::ujson::Event::End(EventToken::Number) => Some(EventResult::ExtractNumber(false)),
56✔
322
        crate::ujson::Event::End(EventToken::NumberAndArray) => {
323
            Some(EventResult::ExtractNumber(true))
140✔
324
        }
325
        crate::ujson::Event::End(EventToken::NumberAndObject) => {
326
            Some(EventResult::ExtractNumber(true))
83✔
327
        }
328

329
        // All other events need parser-specific handling
330
        _ => None,
4,377✔
331
    }
332
}
8,574✔
333

334
/// Process a specific byte through the tokenizer (for cases where byte is already available)
335
pub fn process_byte_through_tokenizer<T: crate::ujson::BitBucket, C: crate::ujson::DepthCounter>(
20,449✔
336
    byte: u8,
20,449✔
337
    tokenizer: &mut crate::ujson::Tokenizer<T, C>,
20,449✔
338
    event_storage: &mut [Option<crate::ujson::Event>; 2],
20,449✔
339
) -> Result<(), ParseError> {
20,449✔
340
    clear_events(event_storage);
20,449✔
341
    let mut callback = create_tokenizer_callback(event_storage);
20,449✔
342
    tokenizer
20,449✔
343
        .parse_chunk(&[byte], &mut callback)
20,449✔
344
        .map_err(ParseError::TokenizerError)?;
20,449✔
345
    Ok(())
20,444✔
346
}
20,449✔
347

348
/// Finish the tokenizer and collect any final events
349
pub fn finish_tokenizer<T: crate::ujson::BitBucket, C: crate::ujson::DepthCounter>(
523✔
350
    tokenizer: &mut crate::ujson::Tokenizer<T, C>,
523✔
351
    event_storage: &mut [Option<crate::ujson::Event>; 2],
523✔
352
) -> Result<(), ParseError> {
523✔
353
    clear_events(event_storage);
523✔
354
    let mut callback = create_tokenizer_callback(event_storage);
523✔
355
    tokenizer
523✔
356
        .finish(&mut callback)
523✔
357
        .map_err(ParseError::TokenizerError)?;
523✔
358
    Ok(())
519✔
359
}
523✔
360

361
#[cfg(test)]
362
mod tests {
363
    use super::*;
364

365
    #[test]
366
    fn test_container_events() {
1✔
367
        assert!(matches!(
1✔
368
            process_simple_events(&crate::ujson::Event::ObjectStart),
1✔
369
            Some(EventResult::Complete(Event::StartObject))
370
        ));
371

372
        assert!(matches!(
1✔
373
            process_simple_events(&crate::ujson::Event::ArrayEnd),
1✔
374
            Some(EventResult::Complete(Event::EndArray))
375
        ));
376
    }
1✔
377

378
    #[test]
379
    fn test_primitive_events() {
1✔
380
        assert!(matches!(
1✔
381
            process_simple_events(&crate::ujson::Event::End(EventToken::True)),
1✔
382
            Some(EventResult::Complete(Event::Bool(true)))
383
        ));
384

385
        assert!(matches!(
1✔
386
            process_simple_events(&crate::ujson::Event::End(EventToken::Null)),
1✔
387
            Some(EventResult::Complete(Event::Null))
388
        ));
389
    }
1✔
390

391
    #[test]
392
    fn test_extraction_triggers() {
1✔
393
        assert!(matches!(
1✔
394
            process_simple_events(&crate::ujson::Event::End(EventToken::String)),
1✔
395
            Some(EventResult::ExtractString)
396
        ));
397

398
        assert!(matches!(
1✔
399
            process_simple_events(&crate::ujson::Event::End(EventToken::Number)),
1✔
400
            Some(EventResult::ExtractNumber(false))
401
        ));
402

403
        assert!(matches!(
1✔
404
            process_simple_events(&crate::ujson::Event::End(EventToken::NumberAndArray)),
1✔
405
            Some(EventResult::ExtractNumber(true))
406
        ));
407
    }
1✔
408

409
    #[test]
410
    fn test_complex_events_not_handled() {
1✔
411
        assert!(process_simple_events(&crate::ujson::Event::Begin(EventToken::String)).is_none());
1✔
412
        assert!(
1✔
413
            process_simple_events(&crate::ujson::Event::Begin(EventToken::EscapeQuote)).is_none()
1✔
414
        );
415
    }
1✔
416

417
    // Mock ContentExtractor for testing
418
    struct MockContentExtractor {
419
        position: usize,
420
        state: crate::shared::State,
421
        string_begin_calls: Vec<usize>,
422
    }
423

424
    impl MockContentExtractor {
425
        fn new() -> Self {
5✔
426
            Self {
5✔
427
                position: 42,
5✔
428
                state: crate::shared::State::None,
5✔
429
                string_begin_calls: Vec::new(),
5✔
430
            }
5✔
431
        }
5✔
432
    }
433

434
    impl EscapeHandler for MockContentExtractor {
435
        fn parser_state(&self) -> &crate::shared::State {
×
436
            &self.state
×
437
        }
×
438

439
        fn process_unicode_escape_with_collector(&mut self) -> Result<(), crate::ParseError> {
×
440
            Ok(())
×
441
        }
×
442

443
        fn handle_simple_escape_char(&mut self, _escape_char: u8) -> Result<(), crate::ParseError> {
×
444
            Ok(())
×
445
        }
×
446
    }
447

448
    impl ContentExtractor for MockContentExtractor {
449
        fn current_position(&self) -> usize {
3✔
450
            self.position
3✔
451
        }
3✔
452

453
        fn begin_string_content(&mut self, pos: usize) {
2✔
454
            self.string_begin_calls.push(pos);
2✔
455
        }
2✔
456

457
        fn parser_state_mut(&mut self) -> &mut crate::shared::State {
3✔
458
            &mut self.state
3✔
459
        }
3✔
460

461
        fn unicode_escape_collector_mut(
×
462
            &mut self,
×
463
        ) -> &mut crate::escape_processor::UnicodeEscapeCollector {
×
464
            unimplemented!("Mock doesn't need unicode collector")
×
465
        }
466

467
        fn extract_string_content(
×
468
            &mut self,
×
469
            _start_pos: usize,
×
470
        ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
×
471
            unimplemented!("Mock doesn't need extraction")
×
472
        }
473

474
        fn extract_key_content(
×
475
            &mut self,
×
476
            _start_pos: usize,
×
477
        ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
×
478
            unimplemented!("Mock doesn't need extraction")
×
479
        }
480

481
        fn extract_number_content(
×
482
            &mut self,
×
483
            _start_pos: usize,
×
484
            _from_container_end: bool,
×
485
        ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
×
486
            unimplemented!("Mock doesn't need extraction")
×
487
        }
488

NEW
489
        fn extract_number(
×
NEW
490
            &mut self,
×
NEW
491
            _start_pos: usize,
×
NEW
492
            _from_container_end: bool,
×
NEW
493
            _finished: bool,
×
NEW
494
        ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
×
NEW
495
            unimplemented!("Mock doesn't need extraction")
×
496
        }
497
    }
498

499
    #[test]
500
    fn test_begin_events_key() {
1✔
501
        let mut context = MockContentExtractor::new();
1✔
502
        let event = crate::ujson::Event::Begin(EventToken::Key);
1✔
503

504
        let result = process_begin_events(&event, &mut context);
1✔
505

506
        assert!(matches!(result, Some(EventResult::Continue)));
1✔
507
        assert!(matches!(context.state, crate::shared::State::Key(42)));
1✔
508
        assert_eq!(context.string_begin_calls, vec![42]);
1✔
509
    }
1✔
510

511
    #[test]
512
    fn test_begin_events_string() {
1✔
513
        let mut context = MockContentExtractor::new();
1✔
514
        let event = crate::ujson::Event::Begin(EventToken::String);
1✔
515

516
        let result = process_begin_events(&event, &mut context);
1✔
517

518
        assert!(matches!(result, Some(EventResult::Continue)));
1✔
519
        assert!(matches!(context.state, crate::shared::State::String(42)));
1✔
520
        assert_eq!(context.string_begin_calls, vec![42]);
1✔
521
    }
1✔
522

523
    #[test]
524
    fn test_begin_events_number() {
1✔
525
        let mut context = MockContentExtractor::new();
1✔
526
        let event = crate::ujson::Event::Begin(EventToken::Number);
1✔
527

528
        let result = process_begin_events(&event, &mut context);
1✔
529

530
        assert!(matches!(result, Some(EventResult::Continue)));
1✔
531
        // Number should get position adjusted by ContentRange::number_start_from_current
532
        assert!(matches!(context.state, crate::shared::State::Number(_)));
1✔
533
        assert_eq!(context.string_begin_calls, Vec::<usize>::new()); // No string calls for numbers
1✔
534
    }
1✔
535

536
    #[test]
537
    fn test_begin_events_primitives() {
1✔
538
        let mut context = MockContentExtractor::new();
1✔
539

540
        for token in [EventToken::True, EventToken::False, EventToken::Null] {
3✔
541
            let event = crate::ujson::Event::Begin(token);
3✔
542
            let result = process_begin_events(&event, &mut context);
3✔
543
            assert!(matches!(result, Some(EventResult::Continue)));
3✔
544
        }
545

546
        // Should not affect state or string processing
547
        assert!(matches!(context.state, crate::shared::State::None));
1✔
548
        assert!(context.string_begin_calls.is_empty());
1✔
549
    }
1✔
550

551
    #[test]
552
    fn test_begin_events_not_handled() {
1✔
553
        let mut context = MockContentExtractor::new();
1✔
554
        let event = crate::ujson::Event::Begin(EventToken::EscapeQuote);
1✔
555

556
        let result = process_begin_events(&event, &mut context);
1✔
557

558
        assert!(result.is_none());
1✔
559
        assert!(matches!(context.state, crate::shared::State::None));
1✔
560
        assert!(context.string_begin_calls.is_empty());
1✔
561
    }
1✔
562

563
    #[test]
564
    fn test_tokenizer_callback() {
1✔
565
        let mut event_storage = [None, None];
1✔
566

567
        // Initially no events
568
        assert!(!have_events(&event_storage));
1✔
569

570
        {
1✔
571
            let mut callback = create_tokenizer_callback(&mut event_storage);
1✔
572

1✔
573
            // Add first event
1✔
574
            callback(crate::ujson::Event::ObjectStart, 1);
1✔
575
        }
1✔
576
        assert!(have_events(&event_storage));
1✔
577
        assert!(event_storage[0].is_some());
1✔
578
        assert!(event_storage[1].is_none());
1✔
579

580
        {
1✔
581
            let mut callback = create_tokenizer_callback(&mut event_storage);
1✔
582
            // Add second event
1✔
583
            callback(crate::ujson::Event::ArrayStart, 1);
1✔
584
        }
1✔
585
        assert!(event_storage[0].is_some());
1✔
586
        assert!(event_storage[1].is_some());
1✔
587

588
        {
1✔
589
            let mut callback = create_tokenizer_callback(&mut event_storage);
1✔
590
            // Storage is full, third event should be ignored (no panic)
1✔
591
            callback(crate::ujson::Event::ObjectEnd, 1);
1✔
592
        }
1✔
593
        assert!(event_storage[0].is_some());
1✔
594
        assert!(event_storage[1].is_some());
1✔
595
    }
1✔
596

597
    #[test]
598
    fn test_event_extraction() {
1✔
599
        let mut event_storage = [
1✔
600
            Some(crate::ujson::Event::ObjectStart),
1✔
601
            Some(crate::ujson::Event::ArrayStart),
1✔
602
        ];
1✔
603

604
        // Extract first event
605
        let first = take_first_event(&mut event_storage);
1✔
606
        assert!(matches!(first, Some(crate::ujson::Event::ObjectStart)));
1✔
607
        assert!(event_storage[0].is_none());
1✔
608
        assert!(event_storage[1].is_some());
1✔
609

610
        // Extract second event
611
        let second = take_first_event(&mut event_storage);
1✔
612
        assert!(matches!(second, Some(crate::ujson::Event::ArrayStart)));
1✔
613
        assert!(event_storage[0].is_none());
1✔
614
        assert!(event_storage[1].is_none());
1✔
615

616
        // No more events
617
        let none = take_first_event(&mut event_storage);
1✔
618
        assert!(none.is_none());
1✔
619
        assert!(!have_events(&event_storage));
1✔
620
    }
1✔
621
}
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