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

kaidokert / picojson-rs / 16297400983

15 Jul 2025 03:20PM UTC coverage: 93.289% (-0.6%) from 93.865%
16297400983

Pull #57

github

web-flow
Merge cc553ac11 into 1dbca311e
Pull Request #57: Even bigger refactor

471 of 539 new or added lines in 8 files covered. (87.38%)

17 existing lines in 5 files now uncovered.

4740 of 5081 relevant lines covered (93.29%)

753.2 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::shared::{ContentRange, State};
9
use crate::ujson::EventToken;
10
use crate::{Event, ParseError};
11

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

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

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

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

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

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

52
/// Process Begin events that have similar patterns between parsers
53
pub fn process_begin_events<C: ContentExtractor>(
4,366✔
54
    event: &crate::ujson::Event,
4,366✔
55
    content_extractor: &mut C,
4,366✔
56
) -> Option<EventResult<'static, 'static>> {
4,366✔
57
    match event {
3,479✔
58
        // String/Key Begin events - nearly identical patterns
59
        crate::ujson::Event::Begin(EventToken::Key) => {
60
            let pos = content_extractor.current_position();
356✔
61
            *content_extractor.parser_state_mut() = State::Key(pos);
356✔
62
            content_extractor.begin_string_content(pos);
356✔
63
            Some(EventResult::Continue)
356✔
64
        }
65
        crate::ujson::Event::Begin(EventToken::String) => {
66
            let pos = content_extractor.current_position();
768✔
67
            *content_extractor.parser_state_mut() = State::String(pos);
768✔
68
            content_extractor.begin_string_content(pos);
768✔
69
            Some(EventResult::Continue)
768✔
70
        }
71

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

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

87
        _ => None,
2,746✔
88
    }
89
}
4,366✔
90

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

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

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

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

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

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

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

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

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

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

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

162
        *self.parser_state_mut() = crate::shared::State::None;
428✔
163
        self.extract_string_content(start_pos)
428✔
164
    }
431✔
165

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

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

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

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

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

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

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

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

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

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

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

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

264
    Ok(())
686✔
265
}
688✔
266

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

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

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

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

328
        // All other events need parser-specific handling
329
        _ => None,
4,361✔
330
    }
331
}
8,555✔
332

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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