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

kaidokert / picojson-rs / 16246666215

13 Jul 2025 07:21AM UTC coverage: 93.785% (+0.1%) from 93.685%
16246666215

Pull #55

github

web-flow
Merge cdb37835c into d470170ad
Pull Request #55: Big refactor

512 of 562 new or added lines in 5 files covered. (91.1%)

1 existing line in 1 file now uncovered.

4693 of 5004 relevant lines covered (93.78%)

678.21 hits per line

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

87.26
/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))
24
    fn begin_escape_sequence(&mut self) -> Result<(), crate::ParseError> {
57✔
25
        Ok(())
57✔
26
    }
57✔
27

28
    /// Append a single literal byte (for per-byte accumulation patterns)
29
    /// Default implementation is no-op - suitable for parsers that don't need per-byte processing
NEW
30
    fn append_literal_byte(&mut self, _byte: u8) -> Result<(), crate::ParseError> {
×
NEW
31
        Ok(())
×
NEW
32
    }
×
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,381✔
53
    event: &crate::ujson::Event,
4,381✔
54
    content_extractor: &mut C,
4,381✔
55
) -> Option<EventResult<'static, 'static>> {
4,381✔
56
    use crate::shared::{ContentRange, State};
57

58
    match event {
3,494✔
59
        // String/Key Begin events - nearly identical patterns
60
        crate::ujson::Event::Begin(EventToken::Key) => {
61
            let pos = content_extractor.current_position();
360✔
62
            *content_extractor.parser_state_mut() = State::Key(pos);
360✔
63
            content_extractor.begin_string_content(pos);
360✔
64
            Some(EventResult::Continue)
360✔
65
        }
66
        crate::ujson::Event::Begin(EventToken::String) => {
67
            let pos = content_extractor.current_position();
770✔
68
            *content_extractor.parser_state_mut() = State::String(pos);
770✔
69
            content_extractor.begin_string_content(pos);
770✔
70
            Some(EventResult::Continue)
770✔
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();
477✔
78
            let number_start = ContentRange::number_start_from_current(pos);
477✔
79
            *content_extractor.parser_state_mut() = State::Number(number_start);
477✔
80
            Some(EventResult::Continue)
477✔
81
        }
82

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

88
        _ => None,
2,746✔
89
    }
90
}
4,381✔
91

92
/// Clear event storage array - utility function
93
pub fn clear_events(event_storage: &mut [Option<crate::ujson::Event>; 2]) {
21,062✔
94
    event_storage[0] = None;
21,062✔
95
    event_storage[1] = None;
21,062✔
96
}
21,062✔
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
    /// Shared validation and extraction for string content
136
    fn validate_and_extract_string(&mut self) -> Result<crate::Event<'_, '_>, crate::ParseError> {
433✔
137
        let start_pos = match *self.parser_state() {
433✔
138
            crate::shared::State::String(pos) => pos,
433✔
NEW
139
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
140
        };
141

142
        // Check for incomplete surrogate pairs before ending the string
143
        if self
433✔
144
            .unicode_escape_collector_mut()
433✔
145
            .has_pending_high_surrogate()
433✔
146
        {
147
            return Err(crate::ParseError::InvalidUnicodeCodepoint);
3✔
148
        }
430✔
149

150
        *self.parser_state_mut() = crate::shared::State::None;
430✔
151
        self.extract_string_content(start_pos)
430✔
152
    }
433✔
153

154
    /// Shared validation and extraction for key content
155
    fn validate_and_extract_key(&mut self) -> Result<crate::Event<'_, '_>, crate::ParseError> {
329✔
156
        let start_pos = match *self.parser_state() {
329✔
157
            crate::shared::State::Key(pos) => pos,
329✔
NEW
158
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
159
        };
160

161
        // Check for incomplete surrogate pairs before ending the key
162
        if self
329✔
163
            .unicode_escape_collector_mut()
329✔
164
            .has_pending_high_surrogate()
329✔
165
        {
NEW
166
            return Err(crate::ParseError::InvalidUnicodeCodepoint);
×
167
        }
329✔
168

169
        *self.parser_state_mut() = crate::shared::State::None;
329✔
170
        self.extract_key_content(start_pos)
329✔
171
    }
329✔
172

173
    /// Shared validation and extraction for number content
174
    fn validate_and_extract_number(
282✔
175
        &mut self,
282✔
176
        from_container_end: bool,
282✔
177
    ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
282✔
178
        let start_pos = match *self.parser_state() {
282✔
179
            crate::shared::State::Number(pos) => pos,
282✔
NEW
180
            _ => return Err(crate::shared::UnexpectedState::StateMismatch.into()),
×
181
        };
182

183
        *self.parser_state_mut() = crate::shared::State::None;
282✔
184
        self.extract_number_content(start_pos, from_container_end)
282✔
185
    }
282✔
186
}
187

188
/// Creates a standard tokenizer callback for event storage
189
///
190
/// This callback stores tokenizer events in the parser's event array, filling the first
191
/// available slot. This pattern is identical across both SliceParser and StreamParser.
192
pub fn create_tokenizer_callback<'a>(
21,065✔
193
    event_storage: &'a mut [Option<crate::ujson::Event>; 2],
21,065✔
194
) -> impl FnMut(crate::ujson::Event, usize) + 'a {
21,065✔
195
    |event, _len| {
8,599✔
196
        for evt in event_storage.iter_mut() {
9,516✔
197
            if evt.is_none() {
9,516✔
198
                *evt = Some(event);
8,598✔
199
                return;
8,598✔
200
            }
918✔
201
        }
202
    }
8,599✔
203
}
21,065✔
204

205
/// Shared utility to check if any events are waiting to be processed
206
pub fn have_events(event_storage: &[Option<crate::ujson::Event>; 2]) -> bool {
48,972✔
207
    event_storage.iter().any(|evt| evt.is_some())
83,197✔
208
}
48,972✔
209

210
/// Shared utility to extract the first available event from storage
211
pub fn take_first_event(
8,588✔
212
    event_storage: &mut [Option<crate::ujson::Event>; 2],
8,588✔
213
) -> Option<crate::ujson::Event> {
8,588✔
214
    event_storage.iter_mut().find_map(|e| e.take())
9,495✔
215
}
8,588✔
216

217
/// Process Begin(EscapeSequence) events using the enhanced lifecycle interface
218
pub fn process_begin_escape_sequence_event<H: EscapeHandler>(
951✔
219
    handler: &mut H,
951✔
220
) -> Result<(), crate::ParseError> {
951✔
221
    // Only process if we're inside a string or key
222
    match handler.parser_state() {
951✔
223
        crate::shared::State::String(_) | crate::shared::State::Key(_) => {
224
            handler.begin_escape_sequence()?;
951✔
225
        }
NEW
226
        _ => {} // Ignore if not in string/key context
×
227
    }
228
    Ok(())
951✔
229
}
951✔
230

231
/// Process simple escape sequence events that have similar patterns between parsers
232
pub fn process_simple_escape_event<C: ContentExtractor>(
688✔
233
    escape_token: &EventToken,
688✔
234
    content_extractor: &mut C,
688✔
235
) -> Result<(), crate::ParseError> {
688✔
236
    // Clear any pending high surrogate state when we encounter a simple escape
237
    // This ensures that interrupted surrogate pairs (like \uD801\n\uDC37) are properly rejected
238
    content_extractor.unicode_escape_collector_mut().reset_all();
688✔
239

240
    // Use unified escape token processing from EscapeProcessor
241
    let unescaped_char =
688✔
242
        crate::escape_processor::EscapeProcessor::process_escape_token(escape_token)?;
688✔
243

244
    // Only process if we're inside a string or key
245
    match content_extractor.parser_state() {
688✔
246
        crate::shared::State::String(_) | crate::shared::State::Key(_) => {
247
            content_extractor.handle_simple_escape_char(unescaped_char)?;
688✔
248
        }
NEW
249
        _ => {} // Ignore if not in string/key context
×
250
    }
251

252
    Ok(())
686✔
253
}
688✔
254

255
/// Process Unicode escape begin/end events that have similar patterns between parsers
256
pub fn process_unicode_escape_events<C: ContentExtractor>(
1,794✔
257
    event: &crate::ujson::Event,
1,794✔
258
    content_extractor: &mut C,
1,794✔
259
) -> Result<bool, crate::ParseError> {
1,794✔
260
    match event {
907✔
261
        crate::ujson::Event::Begin(EventToken::UnicodeEscape) => {
262
            // Start Unicode escape collection - reset collector for new sequence
263
            // Only handle if we're inside a string or key
264
            match content_extractor.parser_state() {
219✔
265
                crate::shared::State::String(_) | crate::shared::State::Key(_) => {
219✔
266
                    content_extractor.unicode_escape_collector_mut().reset();
219✔
267
                }
219✔
NEW
268
                _ => {} // Ignore if not in string/key context
×
269
            }
270
            Ok(true) // Event was handled
219✔
271
        }
272
        crate::ujson::Event::End(EventToken::UnicodeEscape) => {
273
            // Handle end of Unicode escape sequence (\uXXXX)
274
            match content_extractor.parser_state() {
201✔
275
                crate::shared::State::String(_) | crate::shared::State::Key(_) => {
276
                    content_extractor.process_unicode_escape_with_collector()?;
201✔
277
                }
NEW
278
                _ => {} // Ignore if not in string/key context
×
279
            }
280
            Ok(true) // Event was handled
177✔
281
        }
282
        _ => Ok(false), // Event was not handled
1,374✔
283
    }
284
}
1,794✔
285

286
/// Process simple container and primitive events that are identical between parsers
287
pub fn process_simple_events(event: &crate::ujson::Event) -> Option<EventResult<'static, 'static>> {
8,594✔
288
    match event {
1,961✔
289
        // Container events - identical processing
290
        crate::ujson::Event::ObjectStart => Some(EventResult::Complete(Event::StartObject)),
254✔
291
        crate::ujson::Event::ObjectEnd => Some(EventResult::Complete(Event::EndObject)),
209✔
292
        crate::ujson::Event::ArrayStart => Some(EventResult::Complete(Event::StartArray)),
1,672✔
293
        crate::ujson::Event::ArrayEnd => Some(EventResult::Complete(Event::EndArray)),
1,009✔
294

295
        // Primitive values - identical processing
296
        crate::ujson::Event::End(EventToken::True) => {
297
            Some(EventResult::Complete(Event::Bool(true)))
12✔
298
        }
299
        crate::ujson::Event::End(EventToken::False) => {
300
            Some(EventResult::Complete(Event::Bool(false)))
7✔
301
        }
302
        crate::ujson::Event::End(EventToken::Null) => Some(EventResult::Complete(Event::Null)),
8✔
303

304
        // Content extraction triggers - identical logic
305
        crate::ujson::Event::End(EventToken::String) => Some(EventResult::ExtractString),
434✔
306
        crate::ujson::Event::End(EventToken::Key) => Some(EventResult::ExtractKey),
329✔
307
        crate::ujson::Event::End(EventToken::Number) => Some(EventResult::ExtractNumber(false)),
56✔
308
        crate::ujson::Event::End(EventToken::NumberAndArray) => {
309
            Some(EventResult::ExtractNumber(true))
142✔
310
        }
311
        crate::ujson::Event::End(EventToken::NumberAndObject) => {
312
            Some(EventResult::ExtractNumber(true))
86✔
313
        }
314

315
        // All other events need parser-specific handling
316
        _ => None,
4,376✔
317
    }
318
}
8,594✔
319

320
/// Process a specific byte through the tokenizer (for cases where byte is already available)
321
pub fn process_byte_through_tokenizer<T: crate::ujson::BitBucket, C: crate::ujson::DepthCounter>(
20,530✔
322
    byte: u8,
20,530✔
323
    tokenizer: &mut crate::ujson::Tokenizer<T, C>,
20,530✔
324
    event_storage: &mut [Option<crate::ujson::Event>; 2],
20,530✔
325
) -> Result<(), ParseError> {
20,530✔
326
    clear_events(event_storage);
20,530✔
327
    let mut callback = create_tokenizer_callback(event_storage);
20,530✔
328
    tokenizer
20,530✔
329
        .parse_chunk(&[byte], &mut callback)
20,530✔
330
        .map_err(ParseError::TokenizerError)?;
20,530✔
331
    Ok(())
20,525✔
332
}
20,530✔
333

334
/// Finish the tokenizer and collect any final events
335
pub fn finish_tokenizer<T: crate::ujson::BitBucket, C: crate::ujson::DepthCounter>(
532✔
336
    tokenizer: &mut crate::ujson::Tokenizer<T, C>,
532✔
337
    event_storage: &mut [Option<crate::ujson::Event>; 2],
532✔
338
) -> Result<(), ParseError> {
532✔
339
    clear_events(event_storage);
532✔
340
    let mut callback = create_tokenizer_callback(event_storage);
532✔
341
    tokenizer
532✔
342
        .finish(&mut callback)
532✔
343
        .map_err(ParseError::TokenizerError)?;
532✔
344
    Ok(())
528✔
345
}
532✔
346

347
#[cfg(test)]
348
mod tests {
349
    use super::*;
350

351
    #[test]
352
    fn test_container_events() {
1✔
353
        assert!(matches!(
1✔
354
            process_simple_events(&crate::ujson::Event::ObjectStart),
1✔
355
            Some(EventResult::Complete(Event::StartObject))
356
        ));
357

358
        assert!(matches!(
1✔
359
            process_simple_events(&crate::ujson::Event::ArrayEnd),
1✔
360
            Some(EventResult::Complete(Event::EndArray))
361
        ));
362
    }
1✔
363

364
    #[test]
365
    fn test_primitive_events() {
1✔
366
        assert!(matches!(
1✔
367
            process_simple_events(&crate::ujson::Event::End(EventToken::True)),
1✔
368
            Some(EventResult::Complete(Event::Bool(true)))
369
        ));
370

371
        assert!(matches!(
1✔
372
            process_simple_events(&crate::ujson::Event::End(EventToken::Null)),
1✔
373
            Some(EventResult::Complete(Event::Null))
374
        ));
375
    }
1✔
376

377
    #[test]
378
    fn test_extraction_triggers() {
1✔
379
        assert!(matches!(
1✔
380
            process_simple_events(&crate::ujson::Event::End(EventToken::String)),
1✔
381
            Some(EventResult::ExtractString)
382
        ));
383

384
        assert!(matches!(
1✔
385
            process_simple_events(&crate::ujson::Event::End(EventToken::Number)),
1✔
386
            Some(EventResult::ExtractNumber(false))
387
        ));
388

389
        assert!(matches!(
1✔
390
            process_simple_events(&crate::ujson::Event::End(EventToken::NumberAndArray)),
1✔
391
            Some(EventResult::ExtractNumber(true))
392
        ));
393
    }
1✔
394

395
    #[test]
396
    fn test_complex_events_not_handled() {
1✔
397
        assert!(process_simple_events(&crate::ujson::Event::Begin(EventToken::String)).is_none());
1✔
398
        assert!(
1✔
399
            process_simple_events(&crate::ujson::Event::Begin(EventToken::EscapeQuote)).is_none()
1✔
400
        );
401
    }
1✔
402

403
    // Mock ContentExtractor for testing
404
    struct MockContentExtractor {
405
        position: usize,
406
        state: crate::shared::State,
407
        string_begin_calls: Vec<usize>,
408
    }
409

410
    impl MockContentExtractor {
411
        fn new() -> Self {
5✔
412
            Self {
5✔
413
                position: 42,
5✔
414
                state: crate::shared::State::None,
5✔
415
                string_begin_calls: Vec::new(),
5✔
416
            }
5✔
417
        }
5✔
418
    }
419

420
    impl EscapeHandler for MockContentExtractor {
NEW
421
        fn parser_state(&self) -> &crate::shared::State {
×
NEW
422
            &self.state
×
NEW
423
        }
×
424

NEW
425
        fn process_unicode_escape_with_collector(&mut self) -> Result<(), crate::ParseError> {
×
NEW
426
            Ok(())
×
NEW
427
        }
×
428

NEW
429
        fn handle_simple_escape_char(&mut self, _escape_char: u8) -> Result<(), crate::ParseError> {
×
NEW
430
            Ok(())
×
NEW
431
        }
×
432
    }
433

434
    impl ContentExtractor for MockContentExtractor {
435
        fn current_position(&self) -> usize {
3✔
436
            self.position
3✔
437
        }
3✔
438

439
        fn begin_string_content(&mut self, pos: usize) {
2✔
440
            self.string_begin_calls.push(pos);
2✔
441
        }
2✔
442

443
        fn parser_state_mut(&mut self) -> &mut crate::shared::State {
3✔
444
            &mut self.state
3✔
445
        }
3✔
446

NEW
447
        fn unicode_escape_collector_mut(
×
NEW
448
            &mut self,
×
NEW
449
        ) -> &mut crate::escape_processor::UnicodeEscapeCollector {
×
NEW
450
            unimplemented!("Mock doesn't need unicode collector")
×
451
        }
452

NEW
453
        fn extract_string_content(
×
NEW
454
            &mut self,
×
NEW
455
            _start_pos: usize,
×
NEW
456
        ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
×
NEW
457
            unimplemented!("Mock doesn't need extraction")
×
458
        }
459

NEW
460
        fn extract_key_content(
×
NEW
461
            &mut self,
×
NEW
462
            _start_pos: usize,
×
NEW
463
        ) -> Result<crate::Event<'_, '_>, crate::ParseError> {
×
NEW
464
            unimplemented!("Mock doesn't need extraction")
×
465
        }
466

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

476
    #[test]
477
    fn test_begin_events_key() {
1✔
478
        let mut context = MockContentExtractor::new();
1✔
479
        let event = crate::ujson::Event::Begin(EventToken::Key);
1✔
480

481
        let result = process_begin_events(&event, &mut context);
1✔
482

483
        assert!(matches!(result, Some(EventResult::Continue)));
1✔
484
        assert!(matches!(context.state, crate::shared::State::Key(42)));
1✔
485
        assert_eq!(context.string_begin_calls, vec![42]);
1✔
486
    }
1✔
487

488
    #[test]
489
    fn test_begin_events_string() {
1✔
490
        let mut context = MockContentExtractor::new();
1✔
491
        let event = crate::ujson::Event::Begin(EventToken::String);
1✔
492

493
        let result = process_begin_events(&event, &mut context);
1✔
494

495
        assert!(matches!(result, Some(EventResult::Continue)));
1✔
496
        assert!(matches!(context.state, crate::shared::State::String(42)));
1✔
497
        assert_eq!(context.string_begin_calls, vec![42]);
1✔
498
    }
1✔
499

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

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

507
        assert!(matches!(result, Some(EventResult::Continue)));
1✔
508
        // Number should get position adjusted by ContentRange::number_start_from_current
509
        assert!(matches!(context.state, crate::shared::State::Number(_)));
1✔
510
        assert_eq!(context.string_begin_calls, Vec::<usize>::new()); // No string calls for numbers
1✔
511
    }
1✔
512

513
    #[test]
514
    fn test_begin_events_primitives() {
1✔
515
        let mut context = MockContentExtractor::new();
1✔
516

517
        for token in [EventToken::True, EventToken::False, EventToken::Null] {
3✔
518
            let event = crate::ujson::Event::Begin(token);
3✔
519
            let result = process_begin_events(&event, &mut context);
3✔
520
            assert!(matches!(result, Some(EventResult::Continue)));
3✔
521
        }
522

523
        // Should not affect state or string processing
524
        assert!(matches!(context.state, crate::shared::State::None));
1✔
525
        assert!(context.string_begin_calls.is_empty());
1✔
526
    }
1✔
527

528
    #[test]
529
    fn test_begin_events_not_handled() {
1✔
530
        let mut context = MockContentExtractor::new();
1✔
531
        let event = crate::ujson::Event::Begin(EventToken::EscapeQuote);
1✔
532

533
        let result = process_begin_events(&event, &mut context);
1✔
534

535
        assert!(result.is_none());
1✔
536
        assert!(matches!(context.state, crate::shared::State::None));
1✔
537
        assert!(context.string_begin_calls.is_empty());
1✔
538
    }
1✔
539

540
    #[test]
541
    fn test_tokenizer_callback() {
1✔
542
        let mut event_storage = [None, None];
1✔
543

544
        // Initially no events
545
        assert!(!have_events(&event_storage));
1✔
546

547
        {
1✔
548
            let mut callback = create_tokenizer_callback(&mut event_storage);
1✔
549

1✔
550
            // Add first event
1✔
551
            callback(crate::ujson::Event::ObjectStart, 1);
1✔
552
        }
1✔
553
        assert!(have_events(&event_storage));
1✔
554
        assert!(event_storage[0].is_some());
1✔
555
        assert!(event_storage[1].is_none());
1✔
556

557
        {
1✔
558
            let mut callback = create_tokenizer_callback(&mut event_storage);
1✔
559
            // Add second event
1✔
560
            callback(crate::ujson::Event::ArrayStart, 1);
1✔
561
        }
1✔
562
        assert!(event_storage[0].is_some());
1✔
563
        assert!(event_storage[1].is_some());
1✔
564

565
        {
1✔
566
            let mut callback = create_tokenizer_callback(&mut event_storage);
1✔
567
            // Storage is full, third event should be ignored (no panic)
1✔
568
            callback(crate::ujson::Event::ObjectEnd, 1);
1✔
569
        }
1✔
570
        assert!(event_storage[0].is_some());
1✔
571
        assert!(event_storage[1].is_some());
1✔
572
    }
1✔
573

574
    #[test]
575
    fn test_event_extraction() {
1✔
576
        let mut event_storage = [
1✔
577
            Some(crate::ujson::Event::ObjectStart),
1✔
578
            Some(crate::ujson::Event::ArrayStart),
1✔
579
        ];
1✔
580

581
        // Extract first event
582
        let first = take_first_event(&mut event_storage);
1✔
583
        assert!(matches!(first, Some(crate::ujson::Event::ObjectStart)));
1✔
584
        assert!(event_storage[0].is_none());
1✔
585
        assert!(event_storage[1].is_some());
1✔
586

587
        // Extract second event
588
        let second = take_first_event(&mut event_storage);
1✔
589
        assert!(matches!(second, Some(crate::ujson::Event::ArrayStart)));
1✔
590
        assert!(event_storage[0].is_none());
1✔
591
        assert!(event_storage[1].is_none());
1✔
592

593
        // No more events
594
        let none = take_first_event(&mut event_storage);
1✔
595
        assert!(none.is_none());
1✔
596
        assert!(!have_events(&event_storage));
1✔
597
    }
1✔
598
}
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