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

facet-rs / facet / 20188853392

13 Dec 2025 07:30AM UTC coverage: 57.967% (-0.2%) from 58.211%
20188853392

Pull #1270

github

web-flow
Merge 619bf7342 into eba3a00f1
Pull Request #1270: Rewrite FormatDeserializer to use Partial directly

653 of 1100 new or added lines in 4 files covered. (59.36%)

206 existing lines in 4 files now uncovered.

32370 of 55842 relevant lines covered (57.97%)

5679.27 hits per line

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

51.34
/facet-format-json/src/parser.rs
1
extern crate alloc;
2

3
use alloc::{borrow::Cow, vec::Vec};
4

5
use facet_format::{
6
    FieldEvidence, FieldLocationHint, FormatParser, ParseEvent, ProbeStream, ScalarValue,
7
};
8
pub use facet_json::JsonError;
9
use facet_json::{AdapterToken, JsonErrorKind, SliceAdapter, SpannedAdapterToken};
10

11
/// Streaming JSON parser backed by `facet-json`'s `SliceAdapter`.
12
pub struct JsonParser<'de> {
13
    input: &'de [u8],
14
    adapter: SliceAdapter<'de, true>,
15
    stack: Vec<ContextState>,
16
    /// Cached event for `peek_event`.
17
    event_peek: Option<ParseEvent<'de>>,
18
    /// Whether the root value has started.
19
    root_started: bool,
20
    /// Whether the root value has fully completed.
21
    root_complete: bool,
22
    /// Absolute offset (in bytes) of the next unread token.
23
    current_offset: usize,
24
}
25

26
#[derive(Debug)]
27
enum ContextState {
28
    Object(ObjectState),
29
    Array(ArrayState),
30
}
31

32
#[derive(Debug)]
33
enum ObjectState {
34
    KeyOrEnd,
35
    Value,
36
    CommaOrEnd,
37
}
38

39
#[derive(Debug)]
40
enum ArrayState {
41
    ValueOrEnd,
42
    CommaOrEnd,
43
}
44

45
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46
enum DelimKind {
47
    Object,
48
    Array,
49
}
50

51
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52
enum NextAction {
53
    ObjectKey,
54
    ObjectValue,
55
    ObjectComma,
56
    ArrayValue,
57
    ArrayComma,
58
    RootValue,
59
    RootFinished,
60
}
61

62
impl<'de> JsonParser<'de> {
63
    pub fn new(input: &'de [u8]) -> Self {
67✔
64
        Self {
67✔
65
            input,
67✔
66
            adapter: SliceAdapter::new(input),
67✔
67
            stack: Vec::new(),
67✔
68
            event_peek: None,
67✔
69
            root_started: false,
67✔
70
            root_complete: false,
67✔
71
            current_offset: 0,
67✔
72
        }
67✔
73
    }
67✔
74

75
    fn consume_token(&mut self) -> Result<SpannedAdapterToken<'de>, JsonError> {
617✔
76
        let token = self.adapter.next_token().map_err(JsonError::from)?;
617✔
77
        self.current_offset = token.span.offset + token.span.len;
617✔
78
        Ok(token)
617✔
79
    }
617✔
80

81
    fn expect_colon(&mut self) -> Result<(), JsonError> {
123✔
82
        let token = self.consume_token()?;
123✔
83
        if !matches!(token.token, AdapterToken::Colon) {
123✔
84
            return Err(self.unexpected(&token, "':'"));
×
85
        }
123✔
86
        Ok(())
123✔
87
    }
123✔
88

89
    fn parse_value_start_with_token(
213✔
90
        &mut self,
213✔
91
        first: Option<SpannedAdapterToken<'de>>,
213✔
92
    ) -> Result<ParseEvent<'de>, JsonError> {
213✔
93
        let token = match first {
213✔
94
            Some(tok) => tok,
30✔
95
            None => self.consume_token()?,
183✔
96
        };
97

98
        self.root_started = true;
213✔
99

100
        match token.token {
213✔
101
            AdapterToken::ObjectStart => {
102
                self.stack.push(ContextState::Object(ObjectState::KeyOrEnd));
65✔
103
                Ok(ParseEvent::StructStart)
65✔
104
            }
105
            AdapterToken::ArrayStart => {
106
                self.stack.push(ContextState::Array(ArrayState::ValueOrEnd));
10✔
107
                Ok(ParseEvent::SequenceStart)
10✔
108
            }
109
            AdapterToken::String(s) => {
49✔
110
                let event = ParseEvent::Scalar(ScalarValue::Str(s));
49✔
111
                self.finish_value_in_parent();
49✔
112
                Ok(event)
49✔
113
            }
114
            AdapterToken::True => {
115
                self.finish_value_in_parent();
10✔
116
                Ok(ParseEvent::Scalar(ScalarValue::Bool(true)))
10✔
117
            }
118
            AdapterToken::False => {
119
                self.finish_value_in_parent();
2✔
120
                Ok(ParseEvent::Scalar(ScalarValue::Bool(false)))
2✔
121
            }
122
            AdapterToken::Null => {
123
                self.finish_value_in_parent();
3✔
124
                Ok(ParseEvent::Scalar(ScalarValue::Null))
3✔
125
            }
126
            AdapterToken::U64(n) => {
50✔
127
                self.finish_value_in_parent();
50✔
128
                Ok(ParseEvent::Scalar(ScalarValue::U64(n)))
50✔
129
            }
130
            AdapterToken::I64(n) => {
14✔
131
                self.finish_value_in_parent();
14✔
132
                Ok(ParseEvent::Scalar(ScalarValue::I64(n)))
14✔
133
            }
134
            AdapterToken::U128(n) => {
1✔
135
                self.finish_value_in_parent();
1✔
136
                Ok(ParseEvent::Scalar(ScalarValue::Str(Cow::Owned(
1✔
137
                    n.to_string(),
1✔
138
                ))))
1✔
139
            }
140
            AdapterToken::I128(n) => {
1✔
141
                self.finish_value_in_parent();
1✔
142
                Ok(ParseEvent::Scalar(ScalarValue::Str(Cow::Owned(
1✔
143
                    n.to_string(),
1✔
144
                ))))
1✔
145
            }
146
            AdapterToken::F64(n) => {
8✔
147
                self.finish_value_in_parent();
8✔
148
                Ok(ParseEvent::Scalar(ScalarValue::F64(n)))
8✔
149
            }
150
            AdapterToken::ObjectEnd | AdapterToken::ArrayEnd => {
151
                Err(self.unexpected(&token, "value"))
×
152
            }
153
            AdapterToken::Comma | AdapterToken::Colon => Err(self.unexpected(&token, "value")),
×
154
            AdapterToken::Eof => Err(JsonError::new(
×
155
                JsonErrorKind::UnexpectedEof { expected: "value" },
×
156
                token.span,
×
157
            )),
×
158
        }
159
    }
213✔
160

161
    fn finish_value_in_parent(&mut self) {
218✔
162
        if let Some(context) = self.stack.last_mut() {
218✔
163
            match context {
152✔
164
                ContextState::Object(state) => *state = ObjectState::CommaOrEnd,
122✔
165
                ContextState::Array(state) => *state = ArrayState::CommaOrEnd,
30✔
166
            }
167
        } else if self.root_started {
66✔
168
            self.root_complete = true;
66✔
169
        }
66✔
170
    }
218✔
171

172
    fn unexpected(&self, token: &SpannedAdapterToken<'de>, expected: &'static str) -> JsonError {
×
173
        JsonError::new(
×
174
            JsonErrorKind::UnexpectedToken {
×
175
                got: format!("{:?}", token.token),
×
176
                expected,
×
177
            },
×
178
            token.span,
×
179
        )
180
    }
×
181

182
    fn consume_value_tokens(&mut self) -> Result<(), JsonError> {
6✔
183
        let first = self.consume_token()?;
6✔
184
        match first.token {
6✔
185
            AdapterToken::ObjectStart => self.skip_container(DelimKind::Object),
×
186
            AdapterToken::ArrayStart => self.skip_container(DelimKind::Array),
×
187
            AdapterToken::ObjectEnd
188
            | AdapterToken::ArrayEnd
189
            | AdapterToken::Comma
190
            | AdapterToken::Colon => Err(self.unexpected(&first, "value")),
×
191
            AdapterToken::Eof => Err(JsonError::new(
×
192
                JsonErrorKind::UnexpectedEof { expected: "value" },
×
193
                first.span,
×
194
            )),
×
195
            _ => Ok(()),
6✔
196
        }
197
    }
6✔
198

199
    fn skip_container(&mut self, start_kind: DelimKind) -> Result<(), JsonError> {
×
200
        let mut stack = vec![start_kind];
×
201
        while let Some(current) = stack.last().copied() {
×
202
            let token = self.consume_token()?;
×
203
            match token.token {
×
204
                AdapterToken::ObjectStart => stack.push(DelimKind::Object),
×
205
                AdapterToken::ArrayStart => stack.push(DelimKind::Array),
×
206
                AdapterToken::ObjectEnd => {
207
                    if current != DelimKind::Object {
×
208
                        return Err(self.unexpected(&token, "'}'"));
×
209
                    }
×
210
                    stack.pop();
×
211
                    if stack.is_empty() {
×
212
                        break;
×
213
                    }
×
214
                }
215
                AdapterToken::ArrayEnd => {
216
                    if current != DelimKind::Array {
×
217
                        return Err(self.unexpected(&token, "']'"));
×
218
                    }
×
219
                    stack.pop();
×
220
                    if stack.is_empty() {
×
221
                        break;
×
222
                    }
×
223
                }
224
                AdapterToken::Eof => {
225
                    return Err(JsonError::new(
×
226
                        JsonErrorKind::UnexpectedEof { expected: "value" },
×
227
                        token.span,
×
228
                    ));
×
229
                }
230
                _ => {}
×
231
            }
232
        }
233
        Ok(())
×
234
    }
×
235

236
    /// Skip a container in a separate adapter (used during probing).
NEW
237
    fn skip_container_in_adapter(
×
NEW
238
        &self,
×
NEW
239
        adapter: &mut SliceAdapter<'de, true>,
×
NEW
240
        start_kind: DelimKind,
×
NEW
241
    ) -> Result<(), JsonError> {
×
NEW
242
        let mut stack = vec![start_kind];
×
NEW
243
        while let Some(current) = stack.last().copied() {
×
NEW
244
            let token = adapter.next_token().map_err(JsonError::from)?;
×
NEW
245
            match token.token {
×
NEW
246
                AdapterToken::ObjectStart => stack.push(DelimKind::Object),
×
NEW
247
                AdapterToken::ArrayStart => stack.push(DelimKind::Array),
×
248
                AdapterToken::ObjectEnd => {
NEW
249
                    if current != DelimKind::Object {
×
NEW
250
                        return Err(JsonError::new(
×
NEW
251
                            JsonErrorKind::UnexpectedToken {
×
NEW
252
                                got: format!("{:?}", token.token),
×
NEW
253
                                expected: "'}'",
×
NEW
254
                            },
×
NEW
255
                            token.span,
×
NEW
256
                        ));
×
NEW
257
                    }
×
NEW
258
                    stack.pop();
×
NEW
259
                    if stack.is_empty() {
×
NEW
260
                        break;
×
NEW
261
                    }
×
262
                }
263
                AdapterToken::ArrayEnd => {
NEW
264
                    if current != DelimKind::Array {
×
NEW
265
                        return Err(JsonError::new(
×
NEW
266
                            JsonErrorKind::UnexpectedToken {
×
NEW
267
                                got: format!("{:?}", token.token),
×
NEW
268
                                expected: "']'",
×
NEW
269
                            },
×
NEW
270
                            token.span,
×
NEW
271
                        ));
×
NEW
272
                    }
×
NEW
273
                    stack.pop();
×
NEW
274
                    if stack.is_empty() {
×
NEW
275
                        break;
×
NEW
276
                    }
×
277
                }
278
                AdapterToken::Eof => {
NEW
279
                    return Err(JsonError::new(
×
NEW
280
                        JsonErrorKind::UnexpectedEof { expected: "value" },
×
NEW
281
                        token.span,
×
NEW
282
                    ));
×
283
                }
NEW
284
                _ => {}
×
285
            }
286
        }
NEW
287
        Ok(())
×
NEW
288
    }
×
289

290
    fn determine_action(&self) -> NextAction {
488✔
291
        if let Some(context) = self.stack.last() {
488✔
292
            match context {
421✔
293
                ContextState::Object(state) => match state {
361✔
294
                    ObjectState::KeyOrEnd => NextAction::ObjectKey,
123✔
295
                    ObjectState::Value => NextAction::ObjectValue,
116✔
296
                    ObjectState::CommaOrEnd => NextAction::ObjectComma,
122✔
297
                },
298
                ContextState::Array(state) => match state {
60✔
299
                    ArrayState::ValueOrEnd => NextAction::ArrayValue,
30✔
300
                    ArrayState::CommaOrEnd => NextAction::ArrayComma,
30✔
301
                },
302
            }
303
        } else if self.root_complete {
67✔
304
            NextAction::RootFinished
×
305
        } else {
306
            NextAction::RootValue
67✔
307
        }
308
    }
488✔
309

310
    fn produce_event(&mut self) -> Result<ParseEvent<'de>, JsonError> {
410✔
311
        loop {
312
            match self.determine_action() {
488✔
313
                NextAction::ObjectKey => {
314
                    let token = self.consume_token()?;
123✔
315
                    match token.token {
123✔
316
                        AdapterToken::ObjectEnd => {
317
                            self.stack.pop();
×
318
                            self.finish_value_in_parent();
×
319
                            return Ok(ParseEvent::StructEnd);
×
320
                        }
321
                        AdapterToken::String(name) => {
123✔
322
                            self.expect_colon()?;
123✔
323
                            if let Some(ContextState::Object(state)) = self.stack.last_mut() {
123✔
324
                                *state = ObjectState::Value;
123✔
325
                            }
123✔
326
                            return Ok(ParseEvent::FieldKey(name, FieldLocationHint::KeyValue));
123✔
327
                        }
328
                        AdapterToken::Eof => {
329
                            return Err(JsonError::new(
×
330
                                JsonErrorKind::UnexpectedEof {
×
331
                                    expected: "field name or '}'",
×
332
                                },
×
333
                                token.span,
×
334
                            ));
×
335
                        }
336
                        _ => return Err(self.unexpected(&token, "field name or '}'")),
×
337
                    }
338
                }
339
                NextAction::ObjectValue => {
340
                    return self.parse_value_start_with_token(None);
116✔
341
                }
342
                NextAction::ObjectComma => {
343
                    let token = self.consume_token()?;
122✔
344
                    match token.token {
122✔
345
                        AdapterToken::Comma => {
346
                            if let Some(ContextState::Object(state)) = self.stack.last_mut() {
58✔
347
                                *state = ObjectState::KeyOrEnd;
58✔
348
                            }
58✔
349
                            continue;
58✔
350
                        }
351
                        AdapterToken::ObjectEnd => {
352
                            self.stack.pop();
64✔
353
                            self.finish_value_in_parent();
64✔
354
                            return Ok(ParseEvent::StructEnd);
64✔
355
                        }
356
                        AdapterToken::Eof => {
357
                            return Err(JsonError::new(
×
358
                                JsonErrorKind::UnexpectedEof {
×
359
                                    expected: "',' or '}'",
×
360
                                },
×
361
                                token.span,
×
362
                            ));
×
363
                        }
364
                        _ => return Err(self.unexpected(&token, "',' or '}'")),
×
365
                    }
366
                }
367
                NextAction::ArrayValue => {
368
                    let token = self.consume_token()?;
30✔
369
                    match token.token {
30✔
370
                        AdapterToken::ArrayEnd => {
371
                            self.stack.pop();
×
372
                            self.finish_value_in_parent();
×
373
                            return Ok(ParseEvent::SequenceEnd);
×
374
                        }
375
                        AdapterToken::Eof => {
376
                            return Err(JsonError::new(
×
377
                                JsonErrorKind::UnexpectedEof {
×
378
                                    expected: "value or ']'",
×
379
                                },
×
380
                                token.span,
×
381
                            ));
×
382
                        }
383
                        AdapterToken::Comma | AdapterToken::Colon => {
384
                            return Err(self.unexpected(&token, "value or ']'"));
×
385
                        }
386
                        _ => {
387
                            return self.parse_value_start_with_token(Some(token));
30✔
388
                        }
389
                    }
390
                }
391
                NextAction::ArrayComma => {
392
                    let token = self.consume_token()?;
30✔
393
                    match token.token {
30✔
394
                        AdapterToken::Comma => {
395
                            if let Some(ContextState::Array(state)) = self.stack.last_mut() {
20✔
396
                                *state = ArrayState::ValueOrEnd;
20✔
397
                            }
20✔
398
                            continue;
20✔
399
                        }
400
                        AdapterToken::ArrayEnd => {
401
                            self.stack.pop();
10✔
402
                            self.finish_value_in_parent();
10✔
403
                            return Ok(ParseEvent::SequenceEnd);
10✔
404
                        }
405
                        AdapterToken::Eof => {
406
                            return Err(JsonError::new(
×
407
                                JsonErrorKind::UnexpectedEof {
×
408
                                    expected: "',' or ']'",
×
409
                                },
×
410
                                token.span,
×
411
                            ));
×
412
                        }
413
                        _ => return Err(self.unexpected(&token, "',' or ']'")),
×
414
                    }
415
                }
416
                NextAction::RootValue => {
417
                    return self.parse_value_start_with_token(None);
67✔
418
                }
419
                NextAction::RootFinished => {
420
                    return Err(JsonError::without_span(JsonErrorKind::UnexpectedToken {
×
421
                        got: "end of input".into(),
×
422
                        expected: "no additional JSON values",
×
423
                    }));
×
424
                }
425
            }
426
        }
427
    }
410✔
428

429
    fn build_probe(&self) -> Result<Vec<FieldEvidence<'de>>, JsonError> {
6✔
430
        let remaining = self.input.get(self.current_offset..).unwrap_or_default();
6✔
431
        if remaining.is_empty() {
6✔
432
            return Ok(Vec::new());
×
433
        }
6✔
434

435
        let mut adapter = SliceAdapter::<true>::new(remaining);
6✔
436
        let first = adapter.next_token().map_err(JsonError::from)?;
6✔
437
        if !matches!(first.token, AdapterToken::ObjectStart) {
6✔
438
            return Ok(Vec::new());
×
439
        }
6✔
440

441
        let mut evidence = Vec::new();
6✔
442
        loop {
443
            let token = adapter.next_token().map_err(JsonError::from)?;
12✔
444
            match token.token {
12✔
445
                AdapterToken::ObjectEnd => break,
×
446
                AdapterToken::String(name) => {
12✔
447
                    let colon = adapter.next_token().map_err(JsonError::from)?;
12✔
448
                    if !matches!(colon.token, AdapterToken::Colon) {
12✔
449
                        return Err(JsonError::new(
×
450
                            JsonErrorKind::UnexpectedToken {
×
451
                                got: format!("{:?}", colon.token),
×
452
                                expected: "':'",
×
453
                            },
×
454
                            colon.span,
×
455
                        ));
×
456
                    }
12✔
457

458
                    // Capture scalar values, skip complex types (objects/arrays)
459
                    let value_token = adapter.next_token().map_err(JsonError::from)?;
12✔
460
                    let scalar_value = match value_token.token {
12✔
461
                        AdapterToken::String(s) => Some(ScalarValue::Str(s)),
9✔
NEW
462
                        AdapterToken::True => Some(ScalarValue::Bool(true)),
×
NEW
463
                        AdapterToken::False => Some(ScalarValue::Bool(false)),
×
NEW
464
                        AdapterToken::Null => Some(ScalarValue::Null),
×
NEW
465
                        AdapterToken::I64(n) => Some(ScalarValue::I64(n)),
×
466
                        AdapterToken::U64(n) => Some(ScalarValue::U64(n)),
1✔
NEW
467
                        AdapterToken::I128(n) => Some(ScalarValue::Str(Cow::Owned(n.to_string()))),
×
NEW
468
                        AdapterToken::U128(n) => Some(ScalarValue::Str(Cow::Owned(n.to_string()))),
×
469
                        AdapterToken::F64(n) => Some(ScalarValue::F64(n)),
2✔
470
                        AdapterToken::ObjectStart => {
471
                            // Skip the complex object
NEW
472
                            self.skip_container_in_adapter(&mut adapter, DelimKind::Object)?;
×
NEW
473
                            None
×
474
                        }
475
                        AdapterToken::ArrayStart => {
476
                            // Skip the complex array
NEW
477
                            self.skip_container_in_adapter(&mut adapter, DelimKind::Array)?;
×
NEW
478
                            None
×
479
                        }
NEW
480
                        _ => None,
×
481
                    };
482

483
                    if let Some(sv) = scalar_value {
12✔
484
                        evidence.push(FieldEvidence::with_scalar_value(
12✔
485
                            name,
12✔
486
                            FieldLocationHint::KeyValue,
12✔
487
                            None,
12✔
488
                            sv,
12✔
489
                        ));
12✔
490
                    } else {
12✔
NEW
491
                        evidence.push(FieldEvidence::new(name, FieldLocationHint::KeyValue, None));
×
NEW
492
                    }
×
493

494
                    let sep = adapter.next_token().map_err(JsonError::from)?;
12✔
495
                    match sep.token {
12✔
496
                        AdapterToken::Comma => continue,
6✔
497
                        AdapterToken::ObjectEnd => break,
6✔
498
                        AdapterToken::Eof => {
499
                            return Err(JsonError::new(
×
500
                                JsonErrorKind::UnexpectedEof {
×
501
                                    expected: "',' or '}'",
×
502
                                },
×
503
                                sep.span,
×
504
                            ));
×
505
                        }
506
                        _ => {
507
                            return Err(JsonError::new(
×
508
                                JsonErrorKind::UnexpectedToken {
×
509
                                    got: format!("{:?}", sep.token),
×
510
                                    expected: "',' or '}'",
×
511
                                },
×
512
                                sep.span,
×
513
                            ));
×
514
                        }
515
                    }
516
                }
517
                AdapterToken::Eof => {
518
                    return Err(JsonError::new(
×
519
                        JsonErrorKind::UnexpectedEof {
×
520
                            expected: "field name or '}'",
×
521
                        },
×
522
                        token.span,
×
523
                    ));
×
524
                }
525
                _ => {
526
                    return Err(JsonError::new(
×
527
                        JsonErrorKind::UnexpectedToken {
×
528
                            got: format!("{:?}", token.token),
×
529
                            expected: "field name or '}'",
×
530
                        },
×
531
                        token.span,
×
532
                    ));
×
533
                }
534
            }
535
        }
536

537
        Ok(evidence)
6✔
538
    }
6✔
539
}
540

541
impl<'de> FormatParser<'de> for JsonParser<'de> {
542
    type Error = JsonError;
543
    type Probe<'a>
544
        = JsonProbe<'de>
545
    where
546
        Self: 'a;
547

548
    fn next_event(&mut self) -> Result<ParseEvent<'de>, Self::Error> {
410✔
549
        if let Some(event) = self.event_peek.take() {
410✔
550
            return Ok(event);
47✔
551
        }
363✔
552
        self.produce_event()
363✔
553
    }
410✔
554

555
    fn peek_event(&mut self) -> Result<ParseEvent<'de>, Self::Error> {
55✔
556
        if let Some(event) = self.event_peek.clone() {
55✔
557
            return Ok(event);
8✔
558
        }
47✔
559
        let event = self.produce_event()?;
47✔
560
        self.event_peek = Some(event.clone());
47✔
561
        Ok(event)
47✔
562
    }
55✔
563

564
    fn skip_value(&mut self) -> Result<(), Self::Error> {
6✔
565
        debug_assert!(
6✔
566
            self.event_peek.is_none(),
6✔
567
            "skip_value called while an event is buffered"
568
        );
569
        self.consume_value_tokens()?;
6✔
570
        self.finish_value_in_parent();
6✔
571
        Ok(())
6✔
572
    }
6✔
573

574
    fn begin_probe(&mut self) -> Result<Self::Probe<'_>, Self::Error> {
6✔
575
        let evidence = self.build_probe()?;
6✔
576
        Ok(JsonProbe { evidence, idx: 0 })
6✔
577
    }
6✔
578
}
579

580
pub struct JsonProbe<'de> {
581
    evidence: Vec<FieldEvidence<'de>>,
582
    idx: usize,
583
}
584

585
impl<'de> ProbeStream<'de> for JsonProbe<'de> {
586
    type Error = JsonError;
587

588
    fn next(&mut self) -> Result<Option<FieldEvidence<'de>>, Self::Error> {
18✔
589
        if self.idx >= self.evidence.len() {
18✔
590
            Ok(None)
6✔
591
        } else {
592
            let ev = self.evidence[self.idx].clone();
12✔
593
            self.idx += 1;
12✔
594
            Ok(Some(ev))
12✔
595
        }
596
    }
18✔
597
}
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