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

facet-rs / facet / 15113146359

19 May 2025 12:36PM UTC coverage: 56.947% (+0.2%) from 56.733%
15113146359

Pull #628

github

web-flow
Merge 37349d055 into 453013232
Pull Request #628: feat(args): convert reflection spans from arg-wise to char-wise

117 of 183 new or added lines in 5 files covered. (63.93%)

3 existing lines in 2 files now uncovered.

9218 of 16187 relevant lines covered (56.95%)

131.68 hits per line

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

65.22
/facet-deserialize/src/lib.rs
1
#![cfg_attr(not(feature = "std"), no_std)]
2
#![warn(missing_docs)]
3
#![warn(clippy::std_instead_of_core)]
4
#![warn(clippy::std_instead_of_alloc)]
5
#![deny(unsafe_code)]
6
#![doc = include_str!("../README.md")]
7

8
extern crate alloc;
9

10
use alloc::string::ToString;
11
use alloc::{vec, vec::Vec};
12
use core::fmt::Debug;
13

14
mod debug;
15
mod error;
16
use alloc::borrow::Cow;
17
pub use debug::InputDebug;
18

19
pub use error::*;
20

21
mod span;
22
use facet_core::{
23
    Characteristic, Def, Facet, FieldFlags, PointerType, ScalarAffinity, SequenceType, StructKind,
24
    Type, UserType,
25
};
26
use owo_colors::OwoColorize;
27
pub use span::*;
28

29
use facet_reflect::{HeapValue, ReflectError, Wip};
30
use log::trace;
31

32
#[derive(PartialEq, Debug, Clone)]
33
/// A scalar value used during deserialization.
34
/// `u64` and `i64` are separated because `i64` doesn't fit in `u64`,
35
/// but having `u64` is a fast path for 64-bit architectures — no need to
36
/// go through `u128` / `i128` for everything
37
pub enum Scalar<'input> {
38
    /// Owned or borrowed string data.
39
    String(Cow<'input, str>),
40
    /// Unsigned 64-bit integer scalar.
41
    U64(u64),
42
    /// Signed 64-bit integer scalar.
43
    I64(i64),
44
    /// 64-bit floating-point scalar.
45
    F64(f64),
46
    /// Boolean scalar.
47
    Bool(bool),
48
    /// Null scalar (e.g. for formats supporting explicit null).
49
    Null,
50
}
51

52
#[derive(PartialEq, Debug, Clone)]
53
/// Expected next input token or structure during deserialization.
54
pub enum Expectation {
55
    /// Accept a value.
56
    Value,
57
    /// Expect an object key or the end of an object.
58
    ObjectKeyOrObjectClose,
59
    /// Expect a value inside an object.
60
    ObjectVal,
61
    /// Expect a list item or the end of a list.
62
    ListItemOrListClose,
63
}
64

65
#[derive(PartialEq, Debug, Clone)]
66
/// Outcome of parsing the next input element.
67
pub enum Outcome<'input> {
68
    /// Parsed a scalar value.
69
    Scalar(Scalar<'input>),
70
    /// Starting a list/array.
71
    ListStarted,
72
    /// Ending a list/array.
73
    ListEnded,
74
    /// Starting an object/map.
75
    ObjectStarted,
76
    /// Ending an object/map.
77
    ObjectEnded,
78
}
79

80
impl<'input> From<Scalar<'input>> for Outcome<'input> {
81
    fn from(scalar: Scalar<'input>) -> Self {
×
82
        Outcome::Scalar(scalar)
×
83
    }
×
84
}
85

86
use core::fmt;
87

88
/// Display implementation for `Outcome`, focusing on user-friendly descriptions.
89
impl fmt::Display for Outcome<'_> {
90
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148✔
91
        match self {
148✔
92
            Outcome::Scalar(scalar) => write!(f, "scalar {}", scalar),
116✔
93
            Outcome::ListStarted => write!(f, "list start"),
30✔
94
            Outcome::ListEnded => write!(f, "list end"),
×
95
            Outcome::ObjectStarted => write!(f, "object start"),
2✔
96
            Outcome::ObjectEnded => write!(f, "object end"),
×
97
        }
98
    }
148✔
99
}
100

101
/// Display implementation for `Scalar`, for use in displaying `Outcome`.
102
impl fmt::Display for Scalar<'_> {
103
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116✔
104
        match self {
116✔
105
            Scalar::String(s) => write!(f, "string \"{}\"", s),
22✔
106
            Scalar::U64(val) => write!(f, "u64 {}", val),
79✔
107
            Scalar::I64(val) => write!(f, "i64 {}", val),
4✔
108
            Scalar::F64(val) => write!(f, "f64 {}", val),
6✔
109
            Scalar::Bool(val) => write!(f, "bool {}", val),
5✔
110
            Scalar::Null => write!(f, "null"),
×
111
        }
112
    }
116✔
113
}
114

115
impl Outcome<'_> {
116
    fn into_owned(self) -> Outcome<'static> {
×
117
        match self {
×
118
            Outcome::Scalar(scalar) => {
×
119
                let owned_scalar = match scalar {
×
120
                    Scalar::String(cow) => Scalar::String(Cow::Owned(cow.into_owned())),
×
121
                    Scalar::U64(val) => Scalar::U64(val),
×
122
                    Scalar::I64(val) => Scalar::I64(val),
×
123
                    Scalar::F64(val) => Scalar::F64(val),
×
124
                    Scalar::Bool(val) => Scalar::Bool(val),
×
125
                    Scalar::Null => Scalar::Null,
×
126
                };
127
                Outcome::Scalar(owned_scalar)
×
128
            }
129
            Outcome::ListStarted => Outcome::ListStarted,
×
130
            Outcome::ListEnded => Outcome::ListEnded,
×
131
            Outcome::ObjectStarted => Outcome::ObjectStarted,
×
132
            Outcome::ObjectEnded => Outcome::ObjectEnded,
×
133
        }
134
    }
×
135
}
136

137
/// Carries the current parsing state and the in-progress value during deserialization.
138
/// This bundles the mutable context that must be threaded through parsing steps.
139
pub struct NextData<'input, 'facet, 'shape, C = Cooked, I = [u8]>
140
where
141
    'input: 'facet,
142
    I: ?Sized + 'input,
143
{
144
    /// The offset we're supposed to start parsing from
145
    start: usize,
146

147
    /// Controls the parsing flow and stack state.
148
    runner: StackRunner<'input, C, I>,
149

150
    /// Holds the intermediate representation of the value being built.
151
    pub wip: Wip<'facet, 'shape>,
152
}
153

154
impl<'input, 'facet, 'shape, C, I> NextData<'input, 'facet, 'shape, C, I>
155
where
156
    'input: 'facet,
157
    I: ?Sized + 'input,
158
{
159
    /// Returns the input (from the start! not from the current position)
160
    pub fn input(&self) -> &'input I {
969✔
161
        self.runner.input
969✔
162
    }
969✔
163

164
    /// Returns the parsing start offset.
165
    pub fn start(&self) -> usize {
2,140✔
166
        self.start
2,140✔
167
    }
2,140✔
168
}
169

170
/// The result of advancing the parser: updated state and parse outcome or error.
171
pub type NextResult<'input, 'facet, 'shape, T, E, C, I = [u8]> =
172
    (NextData<'input, 'facet, 'shape, C, I>, Result<T, E>);
173

174
/// Trait defining a deserialization format.
175
/// Provides the next parsing step based on current state and expected input.
176
pub trait Format {
177
    /// The kind of input this format consumes, parameterized by input lifetime.
178
    ///
179
    /// * `JsonFmt` => `Input<'input> = [u8]`
180
    /// * `CliFmt`  => `Input<'input> = [&'input str]`
181
    type Input<'input>: ?Sized;
182

183
    /// The type of span used by this format (Raw or Cooked)
184
    type SpanType: Debug + 'static;
185

186
    /// The lowercase source ID of the format, used for error reporting.
187
    fn source(&self) -> &'static str;
188

189
    /// Advance the parser with current state and expectation, producing the next outcome or error.
190
    #[allow(clippy::type_complexity)]
191
    fn next<'input, 'facet, 'shape>(
192
        &mut self,
193
        nd: NextData<'input, 'facet, 'shape, Self::SpanType, Self::Input<'input>>,
194
        expectation: Expectation,
195
    ) -> NextResult<
196
        'input,
197
        'facet,
198
        'shape,
199
        Spanned<Outcome<'input>, Self::SpanType>,
200
        Spanned<DeserErrorKind<'shape>, Self::SpanType>,
201
        Self::SpanType,
202
        Self::Input<'input>,
203
    >
204
    where
205
        'shape: 'input;
206

207
    /// Skip the next value; used to ignore an input.
208
    #[allow(clippy::type_complexity)]
209
    fn skip<'input, 'facet, 'shape>(
210
        &mut self,
211
        nd: NextData<'input, 'facet, 'shape, Self::SpanType, Self::Input<'input>>,
212
    ) -> NextResult<
213
        'input,
214
        'facet,
215
        'shape,
216
        Span<Self::SpanType>,
217
        Spanned<DeserErrorKind<'shape>, Self::SpanType>,
218
        Self::SpanType,
219
        Self::Input<'input>,
220
    >
221
    where
222
        'shape: 'input;
223
}
224

225
/// Trait handling conversion regardless of Format::SpanType to Span<Cooked>
226
pub trait ToCooked<'input, F: Format> {
227
    /// Convert a span to a Cooked span (with byte index over the input, not format-specific index)
228
    fn to_cooked(self, format: &F, input: &'input F::Input<'input>) -> Span<Cooked>;
229
}
230

231
impl<'input, F: Format> ToCooked<'input, F> for Span<Cooked> {
232
    #[inline]
233
    fn to_cooked(self, _format: &F, _input: &'input F::Input<'input>) -> Span<Cooked> {
85✔
234
        self
85✔
235
    }
85✔
236
}
237

238
impl<'input, F: Format<SpanType = Raw, Input<'input> = [&'input str]>> ToCooked<'input, F>
239
    for Span<Raw>
240
{
241
    #[inline]
242
    fn to_cooked(self, _format: &F, input: &'input [&'input str]) -> Span<Cooked> {
19✔
243
        println!("SPECIALIZED: Raw to Cooked conversion for CLI format");
19✔
244

245
        // Calculate start position by summing lengths of preceding args plus spaces
246
        let mut start = 0;
19✔
247
        for arg in input.iter().take(self.start) {
19✔
248
            start += arg.len() + 1; // +1 for space between args
12✔
249
        }
12✔
250

251
        // Length is the length of the current arg
252
        let len = input[self.start].len();
19✔
253

254
        Span::<Cooked>::new(start, len)
19✔
255
    }
19✔
256
}
257

258
/// Instructions guiding the parsing flow, indicating the next expected action or token.
259
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260
pub enum Instruction {
261
    /// Expect a value, specifying the context or reason.
262
    Value(ValueReason),
263
    /// Skip the next value; used to ignore an input.
264
    SkipValue,
265
    /// Indicate completion of a structure or value; triggers popping from stack.
266
    Pop(PopReason),
267
    /// Expect an object key or the end of an object.
268
    ObjectKeyOrObjectClose,
269
    /// Expect a list item or the end of a list.
270
    ListItemOrListClose,
271
}
272

273
/// Reasons for expecting a value, reflecting the current parse context.
274
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275
pub enum ValueReason {
276
    /// Parsing at the root level.
277
    TopLevel,
278
    /// Parsing a value inside an object.
279
    ObjectVal,
280
}
281

282
/// Reasons for popping a state from the stack, indicating why a scope is ended.
283
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284
pub enum PopReason {
285
    /// Ending the top-level parsing scope.
286
    TopLevel,
287
    /// Ending a value within an object.
288
    ObjectVal,
289
    /// Ending value within a list
290
    ListVal,
291
    /// Ending a `Some()` in an option
292
    Some,
293
}
294

295
/// Deserialize a value of type `T` from raw input bytes using format `F`.
296
///
297
/// This function sets up the initial working state and drives the deserialization process,
298
/// ensuring that the resulting value is fully materialized and valid.
299
pub fn deserialize<'input, 'facet, 'shape, T, F>(
197✔
300
    input: &'input F::Input<'input>,
197✔
301
    format: &mut F,
197✔
302
) -> Result<T, DeserError<'input, 'shape, Cooked>>
197✔
303
where
197✔
304
    T: Facet<'facet>,
197✔
305
    F: Format + 'shape,
197✔
306
    F::Input<'input>: InputDebug,
197✔
307
    F::SpanType: core::fmt::Debug,
197✔
308
    Span<F::SpanType>: ToCooked<'input, F>,
197✔
309
    'input: 'facet,
197✔
310
    'shape: 'input,
197✔
311
{
312
    println!("deserialize function entered");
197✔
313

314
    // Run the entire deserialization process and capture any errors
315
    let result: Result<T, DeserError<'input, 'shape, Cooked>> = {
145✔
316
        let source = format.source();
197✔
317

318
        // Step 1: Allocate shape
319
        let wip = match Wip::alloc_shape(T::SHAPE) {
197✔
320
            Ok(wip) => wip,
197✔
NEW
321
            Err(e) => {
×
NEW
322
                let default_span = Span::<F::SpanType>::default();
×
323
                // let cooked_span = cook_span_dispatch!(format, default_span, input);
NEW
324
                let cooked_span = default_span.to_cooked(format, input);
×
NEW
325
                return Err(DeserError::new_reflect(e, input, cooked_span, source));
×
326
            }
327
        };
328

329
        // Step 2: Run deserialize_wip
330
        let heap_value = match deserialize_wip(wip, input, format) {
197✔
331
            Ok(val) => val,
145✔
332
            Err(e) => {
52✔
333
                println!("GOT ERROR FROM deserialize_wip: {:?}", e);
52✔
334

335
                let cooked_span = e.span.to_cooked(format, input);
52✔
336

337
                // Create a completely new error variable with the Cooked type
338
                let cooked_error = DeserError {
52✔
339
                    input: e.input,
52✔
340
                    span: cooked_span,
52✔
341
                    kind: e.kind,
52✔
342
                    source_id: e.source_id,
52✔
343
                };
52✔
344

345
                return Err(cooked_error);
52✔
346
            }
347
        };
348

349
        // Step 3: Materialize
350
        match heap_value.materialize() {
145✔
351
            Ok(val) => Ok(val),
145✔
NEW
352
            Err(e) => {
×
NEW
353
                let default_span = Span::<F::SpanType>::default();
×
NEW
354
                let cooked_span = default_span.to_cooked(format, input);
×
NEW
355
                return Err(DeserError::new_reflect(e, input, cooked_span, source));
×
356
            }
357
        }
358
    };
359

360
    println!("deserialize function post-process");
145✔
361

362
    // Apply span conversion for errors from materialization
363
    match result {
145✔
364
        Ok(value) => {
145✔
365
            println!("Returning OK result");
145✔
366
            Ok(value)
145✔
367
        }
NEW
368
        Err(mut error) => {
×
NEW
369
            println!("Processing error with span conversion");
×
NEW
370
            let new_span = error.span.to_cooked(format, input);
×
371

NEW
372
            if new_span != error.span {
×
NEW
373
                error = DeserError {
×
NEW
374
                    input: error.input,
×
NEW
375
                    span: new_span,
×
NEW
376
                    kind: error.kind,
×
NEW
377
                    source_id: error.source_id,
×
NEW
378
                };
×
NEW
379
            }
×
380

NEW
381
            Err(error)
×
382
        }
383
    }
384
}
197✔
385

386
/// Deserializes a working-in-progress value into a fully materialized heap value.
387
/// This function drives the parsing loop until the entire input is consumed and the value is complete.
388
pub fn deserialize_wip<'input, 'facet, 'shape, F>(
197✔
389
    mut wip: Wip<'facet, 'shape>,
197✔
390
    input: &'input F::Input<'input>,
197✔
391
    format: &mut F,
197✔
392
) -> Result<HeapValue<'facet, 'shape>, DeserError<'input, 'shape, Cooked>>
197✔
393
where
197✔
394
    F: Format + 'shape,
197✔
395
    F::Input<'input>: InputDebug,
197✔
396
    Span<F::SpanType>: ToCooked<'input, F>,
197✔
397
    'input: 'facet,
197✔
398
    'shape: 'input,
197✔
399
{
400
    // This struct is just a bundle of the state that we need to pass around all the time.
401
    let mut runner = StackRunner {
197✔
402
        original_input: input,
197✔
403
        input,
197✔
404
        stack: vec![
197✔
405
            Instruction::Pop(PopReason::TopLevel),
197✔
406
            Instruction::Value(ValueReason::TopLevel),
197✔
407
        ],
197✔
408
        last_span: Span::new(0, 0),
197✔
409
        format_source: format.source(),
197✔
410
    };
197✔
411

412
    macro_rules! next {
413
        ($runner:ident, $wip:ident, $expectation:expr, $method:ident) => {{
414
            let nd = NextData {
415
                start: $runner.last_span.end(), // or supply the appropriate start value if available
416
                runner: $runner,
417
                wip: $wip,
418
            };
419
            let (nd, res) = format.next(nd, $expectation);
420
            $runner = nd.runner;
421
            $wip = nd.wip;
422
            let outcome = res.map_err(|span_kind| {
28✔
423
                $runner.last_span = span_kind.span;
28✔
424
                let error = $runner.err(span_kind.node);
28✔
425
                // Convert the error's span to Cooked
426
                DeserError {
28✔
427
                    input: error.input,
28✔
428
                    span: error.span.to_cooked(format, input),
28✔
429
                    kind: error.kind,
28✔
430
                    source_id: error.source_id,
28✔
431
                }
28✔
432
            })?;
28✔
433
            $runner.last_span = outcome.span;
434
            $wip = $runner.$method($wip, outcome).map_err(|error| {
23✔
435
                DeserError {
23✔
436
                    input:  error.input,
23✔
437
                    span:   error.span.to_cooked(format, input),
23✔
438
                    kind:   error.kind,
23✔
439
                    source_id: error.source_id,
23✔
440
                }
23✔
441
            })?;
23✔
442
        }};
443
    }
444

445
    loop {
446
        let frame_count = wip.frames_count();
1,420✔
447
        debug_assert!(
1,420✔
448
            frame_count
1,420✔
449
                >= runner
1,420✔
450
                    .stack
1,420✔
451
                    .iter()
1,420✔
452
                    .filter(|f| matches!(f, Instruction::Pop(_)))
3,941✔
453
                    .count()
1,420✔
454
        );
455

456
        let insn = match runner.stack.pop() {
1,420✔
457
            Some(insn) => insn,
1,420✔
458
            None => unreachable!("Instruction stack is empty"),
×
459
        };
460

461
        trace!("[{frame_count}] Instruction {:?}", insn.yellow());
1,420✔
462

463
        match insn {
1,420✔
464
            Instruction::Pop(reason) => {
459✔
465
                wip = runner.pop(wip, reason).map_err(|error| {
459✔
466
                    // Convert the error's span to Cooked
NEW
467
                    DeserError {
×
NEW
468
                        input: error.input,
×
NEW
469
                        span: error.span.to_cooked(format, input),
×
NEW
470
                        kind: error.kind,
×
NEW
471
                        source_id: error.source_id,
×
NEW
472
                    }
×
NEW
473
                })?;
×
474

475
                if reason == PopReason::TopLevel {
459✔
476
                    return wip.build().map_err(|e| {
146✔
477
                        let reflect_error = runner.reflect_err(e);
1✔
478
                        // Convert the reflection error's span to Cooked
479
                        DeserError {
1✔
480
                            input: reflect_error.input,
1✔
481
                            span: reflect_error.span.to_cooked(format, input),
1✔
482
                            kind: reflect_error.kind,
1✔
483
                            source_id: reflect_error.source_id,
1✔
484
                        }
1✔
485
                    });
1✔
486
                } else {
487
                    wip = wip.pop().map_err(|e| {
313✔
NEW
488
                        let reflect_error = runner.reflect_err(e);
×
489
                        // Convert the reflection error's span to Cooked
NEW
490
                        DeserError {
×
NEW
491
                            input: reflect_error.input,
×
NEW
492
                            span: reflect_error.span.to_cooked(format, input),
×
NEW
493
                            kind: reflect_error.kind,
×
NEW
494
                            source_id: reflect_error.source_id,
×
NEW
495
                        }
×
NEW
496
                    })?;
×
497
                }
498
            }
499
            Instruction::Value(_why) => {
392✔
500
                let expectation = match _why {
392✔
501
                    ValueReason::TopLevel => Expectation::Value,
197✔
502
                    ValueReason::ObjectVal => Expectation::ObjectVal,
195✔
503
                };
504
                next!(runner, wip, expectation, value);
392✔
505
            }
506
            Instruction::ObjectKeyOrObjectClose => {
507
                next!(
325✔
508
                    runner,
509
                    wip,
510
                    Expectation::ObjectKeyOrObjectClose,
325✔
511
                    object_key_or_object_close
512
                );
513
            }
514
            Instruction::ListItemOrListClose => {
515
                next!(
224✔
516
                    runner,
517
                    wip,
518
                    Expectation::ListItemOrListClose,
224✔
519
                    list_item_or_list_close
520
                );
521
            }
522
            Instruction::SkipValue => {
523
                // Call F::skip to skip over the next value in the input
524
                let nd = NextData {
20✔
525
                    start: runner.last_span.end(),
20✔
526
                    runner,
20✔
527
                    wip,
20✔
528
                };
20✔
529
                let (nd, res) = format.skip(nd);
20✔
530
                runner = nd.runner;
20✔
531
                wip = nd.wip;
20✔
532
                // Only propagate error, don't modify wip, since skip just advances input
533
                let span = res.map_err(|span_kind| {
20✔
534
                    runner.last_span = span_kind.span;
×
NEW
535
                    let error = runner.err(span_kind.node);
×
536
                    // Convert the error's span to Cooked
NEW
537
                    DeserError {
×
NEW
538
                        input: error.input,
×
NEW
539
                        span: error.span.to_cooked(format, input),
×
NEW
540
                        kind: error.kind,
×
NEW
541
                        source_id: error.source_id,
×
NEW
542
                    }
×
UNCOV
543
                })?;
×
544
                // do the actual skip
545
                runner.last_span = span;
20✔
546
            }
547
        }
548
    }
549
}
197✔
550

551
#[doc(hidden)]
552
/// Maintains the parsing state and context necessary to drive deserialization.
553
///
554
/// This struct tracks what the parser expects next, manages input position,
555
/// and remembers the span of the last processed token to provide accurate error reporting.
556
pub struct StackRunner<'input, C = Cooked, I: ?Sized + 'input = [u8]> {
557
    /// A version of the input that doesn't advance as we parse.
558
    pub original_input: &'input I,
559

560
    /// The raw input data being deserialized.
561
    pub input: &'input I,
562

563
    /// Stack of parsing instructions guiding the control flow.
564
    pub stack: Vec<Instruction>,
565

566
    /// Span of the last processed token, for accurate error reporting.
567
    pub last_span: Span<C>,
568

569
    /// Format source identifier for error reporting
570
    pub format_source: &'static str,
571
}
572

573
impl<'input, 'shape, C, I: ?Sized + 'input> StackRunner<'input, C, I>
574
where
575
    I: InputDebug,
576
{
577
    /// Convenience function to create a DeserError using the original input and last_span.
578
    fn err(&self, kind: DeserErrorKind<'shape>) -> DeserError<'input, 'shape, C> {
30✔
579
        DeserError::new(
30✔
580
            kind,
30✔
581
            self.original_input,
30✔
582
            self.last_span,
30✔
583
            self.format_source,
30✔
584
        )
585
    }
30✔
586

587
    /// Convenience function to create a DeserError from a ReflectError,
588
    /// using the original input and last_span for context.
589
    fn reflect_err(&self, err: ReflectError<'shape>) -> DeserError<'input, 'shape, C> {
22✔
590
        DeserError::new_reflect(err, self.original_input, self.last_span, self.format_source)
22✔
591
    }
22✔
592

593
    pub fn pop<'facet>(
459✔
594
        &mut self,
459✔
595
        mut wip: Wip<'facet, 'shape>,
459✔
596
        reason: PopReason,
459✔
597
    ) -> Result<Wip<'facet, 'shape>, DeserError<'input, 'shape, C>> {
459✔
598
        trace!("Popping because {:?}", reason.yellow());
459✔
599

600
        let container_shape = wip.shape();
459✔
601
        match container_shape.ty {
254✔
602
            Type::User(UserType::Struct(sd)) => {
89✔
603
                let mut has_unset = false;
89✔
604

605
                trace!("Let's check all fields are initialized");
89✔
606
                for (index, field) in sd.fields.iter().enumerate() {
161✔
607
                    let is_set = wip.is_field_set(index).map_err(|err| {
161✔
608
                        trace!("Error checking field set status: {:?}", err);
×
609
                        self.reflect_err(err)
×
610
                    })?;
×
611
                    if !is_set {
161✔
612
                        if field.flags.contains(FieldFlags::DEFAULT) {
6✔
613
                            wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
4✔
614
                            if let Some(default_in_place_fn) = field.vtable.default_fn {
4✔
615
                                wip = wip
3✔
616
                                    .put_from_fn(default_in_place_fn)
3✔
617
                                    .map_err(|e| self.reflect_err(e))?;
3✔
618
                                trace!(
3✔
619
                                    "Field #{} {} @ {} was set to default value (via custom fn)",
3✔
620
                                    index.yellow(),
3✔
621
                                    field.name.green(),
3✔
622
                                    field.offset.blue(),
3✔
623
                                );
624
                            } else {
625
                                if !field.shape().is(Characteristic::Default) {
1✔
626
                                    return Err(self.reflect_err(
×
627
                                        ReflectError::DefaultAttrButNoDefaultImpl {
×
628
                                            shape: field.shape(),
×
629
                                        },
×
630
                                    ));
×
631
                                }
1✔
632
                                wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
1✔
633
                                trace!(
1✔
634
                                    "Field #{} {} @ {} was set to default value (via default impl)",
1✔
635
                                    index.yellow(),
1✔
636
                                    field.name.green(),
1✔
637
                                    field.offset.blue(),
1✔
638
                                );
639
                            }
640
                            wip = wip.pop().map_err(|e| self.reflect_err(e))?;
4✔
641
                        } else {
642
                            trace!(
2✔
643
                                "Field #{} {} @ {} is not initialized",
2✔
644
                                index.yellow(),
2✔
645
                                field.name.green(),
2✔
646
                                field.offset.blue(),
2✔
647
                            );
648
                            has_unset = true;
2✔
649
                        }
650
                    }
155✔
651
                }
652

653
                if has_unset && container_shape.has_default_attr() {
89✔
654
                    // let's allocate and build a default value
655
                    let default_val = Wip::alloc_shape(container_shape)
1✔
656
                        .map_err(|e| self.reflect_err(e))?
1✔
657
                        .put_default()
1✔
658
                        .map_err(|e| self.reflect_err(e))?
1✔
659
                        .build()
1✔
660
                        .map_err(|e| self.reflect_err(e))?;
1✔
661
                    let peek = default_val.peek().into_struct().unwrap();
1✔
662

663
                    for (index, field) in sd.fields.iter().enumerate() {
2✔
664
                        let is_set = wip.is_field_set(index).map_err(|err| {
2✔
665
                            trace!("Error checking field set status: {:?}", err);
×
666
                            self.reflect_err(err)
×
667
                        })?;
×
668
                        if !is_set {
2✔
669
                            let address_of_field_from_default = peek.field(index).unwrap().data();
1✔
670
                            wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
1✔
671
                            wip = wip
1✔
672
                                .put_shape(address_of_field_from_default, field.shape())
1✔
673
                                .map_err(|e| self.reflect_err(e))?;
1✔
674
                            wip = wip.pop().map_err(|e| self.reflect_err(e))?;
1✔
675
                        }
1✔
676
                    }
677
                }
88✔
678
            }
679
            Type::User(UserType::Enum(ed)) => {
24✔
680
                trace!("Checking if enum is initialized correctly");
24✔
681

682
                // Check if a variant has been selected
683
                if let Some(variant) = wip.selected_variant() {
24✔
684
                    trace!("Variant {} is selected", variant.name.blue());
24✔
685

686
                    // Check if all fields in the variant are initialized
687
                    if !variant.data.fields.is_empty() {
24✔
688
                        let mut has_unset = false;
16✔
689

690
                        for (index, field) in variant.data.fields.iter().enumerate() {
25✔
691
                            let is_set = wip.is_field_set(index).map_err(|err| {
25✔
692
                                trace!("Error checking field set status: {:?}", err);
×
693
                                self.reflect_err(err)
×
694
                            })?;
×
695

696
                            if !is_set {
25✔
697
                                if field.flags.contains(FieldFlags::DEFAULT) {
×
698
                                    wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
×
699
                                    if let Some(default_in_place_fn) = field.vtable.default_fn {
×
700
                                        wip = wip
×
701
                                            .put_from_fn(default_in_place_fn)
×
702
                                            .map_err(|e| self.reflect_err(e))?;
×
703
                                        trace!(
×
704
                                            "Field #{} @ {} in variant {} was set to default value (via custom fn)",
×
705
                                            index.yellow(),
×
706
                                            field.offset.blue(),
×
707
                                            variant.name
708
                                        );
709
                                    } else {
710
                                        if !field.shape().is(Characteristic::Default) {
×
711
                                            return Err(self.reflect_err(
×
712
                                                ReflectError::DefaultAttrButNoDefaultImpl {
×
713
                                                    shape: field.shape(),
×
714
                                                },
×
715
                                            ));
×
716
                                        }
×
717
                                        wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
×
718
                                        trace!(
×
719
                                            "Field #{} @ {} in variant {} was set to default value (via default impl)",
×
720
                                            index.yellow(),
×
721
                                            field.offset.blue(),
×
722
                                            variant.name
723
                                        );
724
                                    }
725
                                    wip = wip.pop().map_err(|e| self.reflect_err(e))?;
×
726
                                } else {
727
                                    trace!(
×
728
                                        "Field #{} @ {} in variant {} is not initialized",
×
729
                                        index.yellow(),
×
730
                                        field.offset.blue(),
×
731
                                        variant.name
732
                                    );
733
                                    has_unset = true;
×
734
                                }
735
                            }
25✔
736
                        }
737

738
                        if has_unset && container_shape.has_default_attr() {
16✔
739
                            trace!("Enum has DEFAULT attr but variant has uninitialized fields");
×
740
                            // Handle similar to struct, allocate and build default value for variant
741
                            let default_val = Wip::alloc_shape(container_shape)
×
742
                                .map_err(|e| self.reflect_err(e))?
×
743
                                .put_default()
×
744
                                .map_err(|e| self.reflect_err(e))?
×
745
                                .build()
×
746
                                .map_err(|e| self.reflect_err(e))?;
×
747

748
                            let peek = default_val.peek();
×
749
                            let peek_enum = peek.into_enum().map_err(|e| self.reflect_err(e))?;
×
750
                            let default_variant = peek_enum
×
751
                                .active_variant()
×
752
                                .map_err(|e| self.err(DeserErrorKind::VariantError(e)))?;
×
753

754
                            if default_variant == &variant {
×
755
                                // It's the same variant, fill in the missing fields
756
                                for (index, field) in variant.data.fields.iter().enumerate() {
×
757
                                    let is_set = wip.is_field_set(index).map_err(|err| {
×
758
                                        trace!("Error checking field set status: {:?}", err);
×
759
                                        self.reflect_err(err)
×
760
                                    })?;
×
761
                                    if !is_set {
×
762
                                        if let Ok(Some(def_field)) = peek_enum.field(index) {
×
763
                                            wip = wip
×
764
                                                .field(index)
×
765
                                                .map_err(|e| self.reflect_err(e))?;
×
766
                                            wip = wip
×
767
                                                .put_shape(def_field.data(), field.shape())
×
768
                                                .map_err(|e| self.reflect_err(e))?;
×
769
                                            wip = wip.pop().map_err(|e| self.reflect_err(e))?;
×
770
                                        }
×
771
                                    }
×
772
                                }
773
                            }
×
774
                        }
16✔
775
                    }
8✔
776
                } else if container_shape.has_default_attr() {
×
777
                    // No variant selected, but enum has default attribute - set to default
778
                    trace!("No variant selected but enum has DEFAULT attr; setting to default");
×
779
                    let default_val = Wip::alloc_shape(container_shape)
×
780
                        .map_err(|e| self.reflect_err(e))?
×
781
                        .put_default()
×
782
                        .map_err(|e| self.reflect_err(e))?
×
783
                        .build()
×
784
                        .map_err(|e| self.reflect_err(e))?;
×
785

786
                    let peek = default_val.peek();
×
787
                    let peek_enum = peek.into_enum().map_err(|e| self.reflect_err(e))?;
×
788
                    let default_variant_idx = peek_enum
×
789
                        .variant_index()
×
790
                        .map_err(|e| self.err(DeserErrorKind::VariantError(e)))?;
×
791

792
                    // Select the default variant
793
                    wip = wip
×
794
                        .variant(default_variant_idx)
×
795
                        .map_err(|e| self.reflect_err(e))?;
×
796

797
                    // Copy all fields from default value
798
                    let variant = &ed.variants[default_variant_idx];
×
799
                    for (index, field) in variant.data.fields.iter().enumerate() {
×
800
                        if let Ok(Some(def_field)) = peek_enum.field(index) {
×
801
                            wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
×
802
                            wip = wip
×
803
                                .put_shape(def_field.data(), field.shape())
×
804
                                .map_err(|e| self.reflect_err(e))?;
×
805
                            wip = wip.pop().map_err(|e| self.reflect_err(e))?;
×
806
                        }
×
807
                    }
808
                }
×
809
            }
810
            _ => {
811
                trace!(
346✔
812
                    "Thing being popped is not a container I guess (it's a {})",
344✔
813
                    wip.shape()
344✔
814
                );
815
            }
816
        }
817
        Ok(wip)
459✔
818
    }
459✔
819

820
    /// Internal common handler for GotScalar outcome, to deduplicate code.
821
    fn handle_scalar<'facet>(
290✔
822
        &self,
290✔
823
        wip: Wip<'facet, 'shape>,
290✔
824
        scalar: Scalar<'input>,
290✔
825
    ) -> Result<Wip<'facet, 'shape>, DeserError<'input, 'shape, C>>
290✔
826
    where
290✔
827
        'input: 'facet, // 'input outlives 'facet
290✔
828
    {
829
        match scalar {
290✔
830
            Scalar::String(cow) => {
127✔
831
                match wip.innermost_shape().ty {
127✔
832
                    Type::User(UserType::Enum(_)) => {
833
                        if wip.selected_variant().is_some() {
6✔
834
                            // If we already have a variant selected, just put the string
835
                            wip.put(cow.to_string()).map_err(|e| self.reflect_err(e))
×
836
                        } else {
837
                            // Try to select the variant
838
                            match wip.find_variant(&cow) {
6✔
839
                                Some((variant_index, _)) => {
6✔
840
                                    wip.variant(variant_index).map_err(|e| self.reflect_err(e))
6✔
841
                                }
842
                                None => Err(self.err(DeserErrorKind::NoSuchVariant {
×
843
                                    name: cow.to_string(),
×
844
                                    enum_shape: wip.innermost_shape(),
×
845
                                })),
×
846
                            }
847
                        }
848
                    }
849
                    Type::Pointer(PointerType::Reference(_))
850
                        if wip.innermost_shape().is_type::<&str>() =>
1✔
851
                    {
852
                        // This is for handling the &str type
853
                        // The Cow may be Borrowed (we may have an owned string but need a &str)
854
                        match cow {
1✔
855
                            Cow::Borrowed(s) => wip.put(s).map_err(|e| self.reflect_err(e)),
1✔
856
                            Cow::Owned(s) => wip.put(s).map_err(|e| self.reflect_err(e)),
×
857
                        }
858
                    }
859
                    _ => wip.put(cow.to_string()).map_err(|e| self.reflect_err(e)),
120✔
860
                }
861
            }
862
            Scalar::U64(value) => wip.put(value).map_err(|e| self.reflect_err(e)),
127✔
863
            Scalar::I64(value) => wip.put(value).map_err(|e| self.reflect_err(e)),
8✔
864
            Scalar::F64(value) => wip.put(value).map_err(|e| self.reflect_err(e)),
15✔
865
            Scalar::Bool(value) => wip.put(value).map_err(|e| self.reflect_err(e)),
13✔
866
            Scalar::Null => wip.put_default().map_err(|e| self.reflect_err(e)),
×
867
        }
868
    }
290✔
869

870
    /// Handle value parsing
871
    fn value<'facet>(
514✔
872
        &mut self,
514✔
873
        mut wip: Wip<'facet, 'shape>,
514✔
874
        outcome: Spanned<Outcome<'input>, C>,
514✔
875
    ) -> Result<Wip<'facet, 'shape>, DeserError<'input, 'shape, C>>
514✔
876
    where
514✔
877
        'input: 'facet, // 'input must outlive 'facet
514✔
878
    {
879
        trace!(
514✔
880
            "Handling value at {} (innermost {})",
509✔
881
            wip.shape().blue(),
509✔
882
            wip.innermost_shape().yellow()
509✔
883
        );
884

885
        match outcome.node {
296✔
886
            Outcome::Scalar(Scalar::Null) => {
887
                return wip.put_default().map_err(|e| self.reflect_err(e));
6✔
888
            }
889
            _ => {
890
                if matches!(wip.shape().def, Def::Option(_)) {
508✔
891
                    // TODO: Update option handling
892
                    trace!("Starting Some(_) option for {}", wip.shape().blue());
8✔
893
                    wip = wip.push_some().map_err(|e| self.reflect_err(e))?;
8✔
894
                    self.stack.push(Instruction::Pop(PopReason::Some));
8✔
895
                }
500✔
896
            }
897
        }
898

899
        match outcome.node {
508✔
900
            Outcome::Scalar(s) => {
290✔
901
                wip = self.handle_scalar(wip, s)?;
290✔
902
            }
903
            Outcome::ListStarted => {
904
                let shape = wip.innermost_shape();
92✔
905
                match shape.def {
92✔
906
                    Def::Array(_) => {
907
                        trace!("Array starting for array ({})!", shape.blue());
10✔
908
                        // We'll initialize the array elements one by one through the pushback workflow
909
                        // Don't call put_default, as arrays need different initialization
910
                    }
911
                    Def::Slice(_) => {
912
                        trace!("Array starting for slice ({})!", shape.blue());
×
913
                    }
914
                    Def::List(_) => {
915
                        trace!("Array starting for list ({})!", shape.blue());
17✔
916
                        wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
17✔
917
                    }
918
                    Def::Scalar(sd) => {
12✔
919
                        if matches!(sd.affinity, ScalarAffinity::Empty(_)) {
12✔
920
                            trace!("Empty tuple/scalar, nice");
12✔
921
                            wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
12✔
922
                        } else {
923
                            return Err(self.err(DeserErrorKind::UnsupportedType {
×
924
                                got: shape,
×
925
                                wanted: "array, list, tuple, or slice",
×
926
                            }));
×
927
                        }
928
                    }
929
                    _ => {
930
                        // For non-collection types, check the Type enum
931
                        if let Type::User(user_ty) = shape.ty {
53✔
932
                            match user_ty {
3✔
933
                                UserType::Enum(_) => {
934
                                    trace!("Array starting for enum ({})!", shape.blue());
3✔
935
                                }
936
                                UserType::Struct(_) => {
937
                                    trace!("Array starting for tuple struct ({})!", shape.blue());
×
938
                                    wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
×
939
                                }
940
                                _ => {
941
                                    return Err(self.err(DeserErrorKind::UnsupportedType {
×
942
                                        got: shape,
×
943
                                        wanted: "array, list, tuple, or slice",
×
944
                                    }));
×
945
                                }
946
                            }
947
                        } else if let Type::Sequence(SequenceType::Tuple(tuple_type)) = shape.ty {
50✔
948
                            trace!(
50✔
949
                                "Array starting for tuple ({}) with {} fields!",
50✔
950
                                shape.blue(),
50✔
951
                                tuple_type.fields.len()
50✔
952
                            );
953
                            // Initialize the tuple with default values
954
                            wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
50✔
955
                            // No special handling needed here - the tuple is already set up correctly
956
                            // and will receive array elements via pushback
957
                        } else {
958
                            return Err(self.err(DeserErrorKind::UnsupportedType {
×
959
                                got: shape,
×
960
                                wanted: "array, list, tuple, or slice",
×
961
                            }));
×
962
                        }
963
                    }
964
                }
965
                trace!("Beginning pushback");
92✔
966
                self.stack.push(Instruction::ListItemOrListClose);
92✔
967
                wip = wip.begin_pushback().map_err(|e| self.reflect_err(e))?;
92✔
968
            }
969
            Outcome::ListEnded => {
970
                trace!("List closing");
×
971
                wip = wip.pop().map_err(|e| self.reflect_err(e))?;
×
972
            }
973
            Outcome::ObjectStarted => {
974
                let shape = wip.innermost_shape();
126✔
975
                match shape.def {
126✔
976
                    Def::Map(_md) => {
2✔
977
                        trace!("Object starting for map value ({})!", shape.blue());
2✔
978
                        wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
2✔
979
                    }
980
                    _ => {
981
                        // For non-collection types, check the Type enum
982
                        if let Type::User(user_ty) = shape.ty {
124✔
983
                            match user_ty {
124✔
984
                                UserType::Enum(_) => {
985
                                    trace!("Object starting for enum value ({})!", shape.blue());
20✔
986
                                    // nothing to do here
987
                                }
988
                                UserType::Struct(_) => {
989
                                    trace!("Object starting for struct value ({})!", shape.blue());
104✔
990
                                    // nothing to do here
991
                                }
992
                                _ => {
993
                                    return Err(self.err(DeserErrorKind::UnsupportedType {
×
994
                                        got: shape,
×
995
                                        wanted: "map, enum, or struct",
×
996
                                    }));
×
997
                                }
998
                            }
999
                        } else if let Type::Sequence(SequenceType::Tuple(tuple_type)) = shape.ty {
×
1000
                            // This could be a tuple that was serialized as an object
1001
                            // Despite this being unusual, we'll handle it here for robustness
1002
                            trace!(
×
1003
                                "Object starting for tuple ({}) with {} fields - unusual but handling",
×
1004
                                shape.blue(),
×
1005
                                tuple_type.fields.len()
×
1006
                            );
1007
                            // Initialize the tuple with default values
1008
                            wip = wip.put_default().map_err(|e| self.reflect_err(e))?;
×
1009
                        } else {
1010
                            return Err(self.err(DeserErrorKind::UnsupportedType {
×
1011
                                got: shape,
×
1012
                                wanted: "map, enum, struct, or tuple",
×
1013
                            }));
×
1014
                        }
1015
                    }
1016
                }
1017

1018
                self.stack.push(Instruction::ObjectKeyOrObjectClose);
126✔
1019
            }
1020
            Outcome::ObjectEnded => todo!(),
×
1021
        }
1022
        Ok(wip)
494✔
1023
    }
514✔
1024

1025
    fn object_key_or_object_close<'facet>(
318✔
1026
        &mut self,
318✔
1027
        mut wip: Wip<'facet, 'shape>,
318✔
1028
        outcome: Spanned<Outcome<'input>, C>,
318✔
1029
    ) -> Result<Wip<'facet, 'shape>, DeserError<'input, 'shape, C>>
318✔
1030
    where
318✔
1031
        'input: 'facet,
318✔
1032
    {
1033
        match outcome.node {
217✔
1034
            Outcome::Scalar(Scalar::String(key)) => {
217✔
1035
                trace!("Parsed object key: {}", key.cyan());
217✔
1036

1037
                let mut ignore = false;
217✔
1038
                let mut needs_pop = true;
217✔
1039
                let mut handled_by_flatten = false;
217✔
1040

1041
                let shape = wip.innermost_shape();
217✔
1042
                match shape.ty {
217✔
1043
                    Type::User(UserType::Struct(sd)) => {
188✔
1044
                        // First try to find a direct field match
1045
                        if let Some(index) = wip.field_index(&key) {
188✔
1046
                            trace!("It's a struct field");
163✔
1047
                            wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
163✔
1048
                        } else {
1049
                            // Check for flattened fields
1050
                            let mut found_in_flatten = false;
25✔
1051
                            for (index, field) in sd.fields.iter().enumerate() {
37✔
1052
                                if field.flags.contains(FieldFlags::FLATTEN) {
37✔
1053
                                    trace!("Found flattened field #{}", index);
6✔
1054
                                    // Enter the flattened field
1055
                                    wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
6✔
1056

1057
                                    // Check if this flattened field has the requested key
1058
                                    if let Some(subfield_index) = wip.field_index(&key) {
6✔
1059
                                        trace!("Found key {} in flattened field", key);
3✔
1060
                                        wip = wip
3✔
1061
                                            .field(subfield_index)
3✔
1062
                                            .map_err(|e| self.reflect_err(e))?;
3✔
1063
                                        found_in_flatten = true;
3✔
1064
                                        handled_by_flatten = true;
3✔
1065
                                        break;
3✔
1066
                                    } else if let Some((_variant_index, _variant)) =
×
1067
                                        wip.find_variant(&key)
3✔
1068
                                    {
1069
                                        trace!("Found key {} in flattened field", key);
×
1070
                                        wip = wip
×
1071
                                            .variant_named(&key)
×
1072
                                            .map_err(|e| self.reflect_err(e))?;
×
1073
                                        found_in_flatten = true;
×
1074
                                        break;
×
1075
                                    } else {
1076
                                        // Key not in this flattened field, go back up
1077
                                        wip = wip.pop().map_err(|e| self.reflect_err(e))?;
3✔
1078
                                    }
1079
                                }
31✔
1080
                            }
1081

1082
                            if !found_in_flatten {
25✔
1083
                                if wip.shape().has_deny_unknown_fields_attr() {
22✔
1084
                                    trace!(
2✔
1085
                                        "It's not a struct field AND we're denying unknown fields"
2✔
1086
                                    );
1087
                                    return Err(self.err(DeserErrorKind::UnknownField {
2✔
1088
                                        field_name: key.to_string(),
2✔
1089
                                        shape: wip.shape(),
2✔
1090
                                    }));
2✔
1091
                                } else {
1092
                                    trace!(
20✔
1093
                                        "It's not a struct field and we're ignoring unknown fields"
20✔
1094
                                    );
1095
                                    ignore = true;
20✔
1096
                                }
1097
                            }
3✔
1098
                        }
1099
                    }
1100
                    Type::User(UserType::Enum(_ed)) => match wip.find_variant(&key) {
26✔
1101
                        Some((index, variant)) => {
14✔
1102
                            trace!(
14✔
1103
                                "Selecting variant {}::{}",
14✔
1104
                                wip.shape().blue(),
14✔
1105
                                variant.name.yellow(),
14✔
1106
                            );
1107
                            wip = wip.variant(index).map_err(|e| self.reflect_err(e))?;
14✔
1108

1109
                            // Let's see what's in the variant — if it's tuple-like with only one field, we want to push field 0
1110
                            if matches!(variant.data.kind, StructKind::Tuple)
14✔
1111
                                && variant.data.fields.len() == 1
8✔
1112
                            {
1113
                                trace!(
5✔
1114
                                    "Tuple variant {}::{} encountered, pushing field 0",
5✔
1115
                                    wip.shape().blue(),
5✔
1116
                                    variant.name.yellow()
5✔
1117
                                );
1118
                                wip = wip.field(0).map_err(|e| self.reflect_err(e))?;
5✔
1119
                                self.stack.push(Instruction::Pop(PopReason::ObjectVal));
5✔
1120
                            }
9✔
1121

1122
                            needs_pop = false;
14✔
1123
                        }
1124
                        None => {
1125
                            if let Some(_variant_index) = wip.selected_variant() {
12✔
1126
                                trace!(
12✔
1127
                                    "Already have a variant selected, treating {} as struct field of {}::{}",
12✔
1128
                                    key,
1129
                                    wip.shape().blue(),
12✔
1130
                                    wip.selected_variant().unwrap().name.yellow(),
12✔
1131
                                );
1132
                                // Try to find the field index of the key within the selected variant
1133
                                if let Some(index) = wip.field_index(&key) {
12✔
1134
                                    trace!("Found field {} in selected variant", key.blue());
12✔
1135
                                    wip = wip.field(index).map_err(|e| self.reflect_err(e))?;
12✔
1136
                                } else if wip.shape().has_deny_unknown_fields_attr() {
×
1137
                                    trace!("Unknown field in variant and denying unknown fields");
×
1138
                                    return Err(self.err(DeserErrorKind::UnknownField {
×
1139
                                        field_name: key.to_string(),
×
1140
                                        shape: wip.shape(),
×
1141
                                    }));
×
1142
                                } else {
1143
                                    trace!(
×
1144
                                        "Ignoring unknown field '{}' in variant '{}::{}'",
×
1145
                                        key,
1146
                                        wip.shape(),
×
1147
                                        wip.selected_variant().unwrap().name
×
1148
                                    );
1149
                                    ignore = true;
×
1150
                                }
1151
                            } else {
1152
                                return Err(self.err(DeserErrorKind::NoSuchVariant {
×
1153
                                    name: key.to_string(),
×
1154
                                    enum_shape: wip.shape(),
×
1155
                                }));
×
1156
                            }
1157
                        }
1158
                    },
1159
                    _ => {
1160
                        // Check if it's a map
1161
                        if let Def::Map(_) = shape.def {
3✔
1162
                            wip = wip.push_map_key().map_err(|e| self.reflect_err(e))?;
3✔
1163
                            wip = wip.put(key.to_string()).map_err(|e| self.reflect_err(e))?;
3✔
1164
                            wip = wip.push_map_value().map_err(|e| self.reflect_err(e))?;
3✔
1165
                        } else {
1166
                            return Err(self.err(DeserErrorKind::Unimplemented(
×
1167
                                "object key for non-struct/map",
×
1168
                            )));
×
1169
                        }
1170
                    }
1171
                }
1172

1173
                self.stack.push(Instruction::ObjectKeyOrObjectClose);
215✔
1174
                if ignore {
215✔
1175
                    self.stack.push(Instruction::SkipValue);
20✔
1176
                } else {
20✔
1177
                    if needs_pop && !handled_by_flatten {
195✔
1178
                        trace!("Pushing Pop insn to stack (ObjectVal)");
178✔
1179
                        self.stack.push(Instruction::Pop(PopReason::ObjectVal));
178✔
1180
                    } else if handled_by_flatten {
17✔
1181
                        // We need two pops for flattened fields - one for the field itself,
1182
                        // one for the containing struct
1183
                        trace!("Pushing Pop insn to stack (ObjectVal) for flattened field");
3✔
1184
                        self.stack.push(Instruction::Pop(PopReason::ObjectVal));
3✔
1185
                        self.stack.push(Instruction::Pop(PopReason::ObjectVal));
3✔
1186
                    }
14✔
1187
                    self.stack.push(Instruction::Value(ValueReason::ObjectVal));
195✔
1188
                }
1189
                Ok(wip)
215✔
1190
            }
1191
            Outcome::ObjectEnded => {
1192
                trace!("Object closing");
101✔
1193
                Ok(wip)
101✔
1194
            }
1195
            _ => Err(self.err(DeserErrorKind::UnexpectedOutcome {
×
1196
                got: outcome.node.into_owned(),
×
1197
                wanted: "scalar or object close",
×
1198
            })),
×
1199
        }
1200
    }
318✔
1201

1202
    fn list_item_or_list_close<'facet>(
222✔
1203
        &mut self,
222✔
1204
        mut wip: Wip<'facet, 'shape>,
222✔
1205
        outcome: Spanned<Outcome<'input>, C>,
222✔
1206
    ) -> Result<Wip<'facet, 'shape>, DeserError<'input, 'shape, C>>
222✔
1207
    where
222✔
1208
        'input: 'facet,
222✔
1209
    {
1210
        match outcome.node {
222✔
1211
            Outcome::ListEnded => {
1212
                trace!("List close");
74✔
1213
                Ok(wip)
74✔
1214
            }
1215
            _ => {
1216
                self.stack.push(Instruction::ListItemOrListClose);
148✔
1217
                self.stack.push(Instruction::Pop(PopReason::ListVal));
148✔
1218

1219
                trace!(
148✔
1220
                    "Expecting list item, doing a little push before doing value with outcome {}",
148✔
1221
                    outcome.magenta()
148✔
1222
                );
1223
                trace!("Before push, wip.shape is {}", wip.shape().blue());
148✔
1224

1225
                // Special handling for tuples - we need to identify if we're in a tuple context
1226
                let is_tuple = matches!(
148✔
1227
                    wip.innermost_shape().ty,
148✔
1228
                    Type::Sequence(SequenceType::Tuple(_))
1229
                );
1230

1231
                if is_tuple {
148✔
1232
                    trace!("Handling list item for a tuple type");
91✔
1233
                    // For tuples, we need to use field-based access by index
1234
                    wip = wip.push().map_err(|e| self.reflect_err(e))?;
91✔
1235
                } else {
1236
                    // Standard list/array handling
1237
                    wip = wip.push().map_err(|e| self.reflect_err(e))?;
57✔
1238
                }
1239

1240
                trace!(" After push, wip.shape is {}", wip.shape().cyan());
141✔
1241
                wip = self.value(wip, outcome)?;
141✔
1242
                Ok(wip)
138✔
1243
            }
1244
        }
1245
    }
222✔
1246
}
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