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

facet-rs / facet / 20022135602

08 Dec 2025 08:50AM UTC coverage: 58.569% (-0.03%) from 58.6%
20022135602

Pull #1176

github

web-flow
Merge 1a6723e6f into 24ecc6001
Pull Request #1176: fix: implement untagged enum deserialization in facet-json

70 of 149 new or added lines in 4 files covered. (46.98%)

1379 existing lines in 11 files now uncovered.

24750 of 42258 relevant lines covered (58.57%)

544.36 hits per line

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

75.0
/facet-reflect/src/partial/partial_api/misc.rs
1
use super::*;
2

3
////////////////////////////////////////////////////////////////////////////////////////////////////
4
// Misc.
5
////////////////////////////////////////////////////////////////////////////////////////////////////
6
impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
7
    /// Returns true if the Partial is in an active state (not built or poisoned).
8
    ///
9
    /// After `build()` succeeds or after an error causes poisoning, the Partial
10
    /// becomes inactive and most operations will fail.
11
    #[inline]
12
    pub fn is_active(&self) -> bool {
×
13
        self.state == PartialState::Active
×
14
    }
×
15

16
    /// Returns the current frame count (depth of nesting)
17
    ///
18
    /// The initial frame count is 1 — `begin_field` would push a new frame,
19
    /// bringing it to 2, then `end` would bring it back to `1`.
20
    ///
21
    /// This is an implementation detail of `Partial`, kinda, but deserializers
22
    /// might use this for debug assertions, to make sure the state is what
23
    /// they think it is.
24
    #[inline]
25
    pub fn frame_count(&self) -> usize {
4✔
26
        self.frames().len()
4✔
27
    }
4✔
28

29
    /// Returns the shape of the current frame.
30
    ///
31
    /// # Panics
32
    ///
33
    /// Panics if the Partial has been poisoned or built, or if there are no frames
34
    /// (which indicates a bug in the Partial implementation).
35
    #[inline]
36
    pub fn shape(&self) -> &'static Shape {
35,349✔
37
        if self.state != PartialState::Active {
35,349✔
38
            panic!(
×
39
                "Partial::shape() called on non-active Partial (state: {:?})",
40
                self.state
41
            );
42
        }
35,349✔
43
        self.frames()
35,349✔
44
            .last()
35,349✔
45
            .expect("Partial::shape() called but no frames exist - this is a bug")
35,349✔
46
            .shape
35,349✔
47
    }
35,349✔
48

49
    /// Checks if the current (top) frame is fully initialized.
50
    ///
51
    /// This is useful for untagged enum deserialization where we need to validate
52
    /// that all required fields were set before considering a variant match successful.
53
    ///
54
    /// Returns `Ok(())` if fully initialized, or an error describing what's missing.
55
    #[inline]
56
    pub fn check_fully_initialized(&self) -> Result<(), ReflectError> {
5✔
57
        if self.state != PartialState::Active {
5✔
NEW
58
            return Err(ReflectError::InvariantViolation {
×
NEW
59
                invariant: "check_fully_initialized() called on non-active Partial",
×
NEW
60
            });
×
61
        }
5✔
62
        let frame = self
5✔
63
            .frames()
5✔
64
            .last()
5✔
65
            .ok_or(ReflectError::InvariantViolation {
5✔
66
                invariant: "check_fully_initialized() called but no frames exist",
5✔
67
            })?;
5✔
68
        frame.require_full_initialization()
5✔
69
    }
5✔
70

71
    /// Returns the shape of the current frame, or `None` if the Partial is
72
    /// inactive (poisoned or built) or has no frames.
73
    ///
74
    /// This is useful for debugging/logging where you want to inspect the state
75
    /// without risking a panic.
76
    #[inline]
UNCOV
77
    pub fn try_shape(&self) -> Option<&'static Shape> {
×
UNCOV
78
        if self.state != PartialState::Active {
×
UNCOV
79
            return None;
×
UNCOV
80
        }
×
UNCOV
81
        self.frames().last().map(|f| f.shape)
×
UNCOV
82
    }
×
83

84
    /// Returns the current deferred resolution, if in deferred mode.
85
    #[inline]
86
    pub fn deferred_resolution(&self) -> Option<&Resolution> {
3✔
87
        self.resolution()
3✔
88
    }
3✔
89

90
    /// Returns the current path in deferred mode as a slice (for debugging/tracing).
91
    #[inline]
UNCOV
92
    pub fn current_path_slice(&self) -> Option<&[&'static str]> {
×
UNCOV
93
        self.current_path().map(|p| p.as_slice())
×
UNCOV
94
    }
×
95

96
    /// Enables deferred materialization mode with the given Resolution.
97
    ///
98
    /// When deferred mode is enabled:
99
    /// - `end()` stores frames instead of validating them
100
    /// - Re-entering a path restores the stored frame with its state intact
101
    /// - `finish_deferred()` performs final validation and materialization
102
    ///
103
    /// This allows deserializers to handle interleaved fields (e.g., TOML dotted
104
    /// keys, flattened structs) where nested fields aren't contiguous in the input.
105
    ///
106
    /// # Use Cases
107
    ///
108
    /// - TOML dotted keys: `inner.x = 1` followed by `count = 2` then `inner.y = 3`
109
    /// - Flattened structs where nested fields appear at the parent level
110
    /// - Any format where field order doesn't match struct nesting
111
    ///
112
    /// # Errors
113
    ///
114
    /// Returns an error if already in deferred mode.
115
    #[inline]
116
    pub fn begin_deferred(mut self, resolution: Resolution) -> Result<Self, ReflectError> {
432✔
117
        // Cannot enable deferred mode if already in deferred mode
118
        if self.is_deferred() {
432✔
119
            return Err(ReflectError::InvariantViolation {
1✔
120
                invariant: "begin_deferred() called but already in deferred mode",
1✔
121
            });
1✔
122
        }
431✔
123

124
        // Take the stack out of Strict mode and wrap in Deferred mode
125
        let FrameMode::Strict { stack } = core::mem::replace(
431✔
126
            &mut self.mode,
431✔
127
            FrameMode::Strict { stack: Vec::new() }, // temporary placeholder
431✔
128
        ) else {
431✔
UNCOV
129
            unreachable!("just checked we're not in deferred mode");
×
130
        };
131

132
        let start_depth = stack.len();
431✔
133
        self.mode = FrameMode::Deferred {
431✔
134
            stack,
431✔
135
            resolution,
431✔
136
            start_depth,
431✔
137
            current_path: Vec::new(),
431✔
138
            stored_frames: BTreeMap::new(),
431✔
139
        };
431✔
140
        Ok(self)
431✔
141
    }
432✔
142

143
    /// Finishes deferred mode: validates all stored frames and finalizes.
144
    ///
145
    /// This method:
146
    /// 1. Validates that all stored frames are fully initialized
147
    /// 2. Processes frames from deepest to shallowest, updating parent ISets
148
    /// 3. Validates the root frame
149
    ///
150
    /// # Errors
151
    ///
152
    /// Returns an error if any required fields are missing or if the partial is
153
    /// not in deferred mode.
154
    pub fn finish_deferred(mut self) -> Result<Self, ReflectError> {
350✔
155
        // Check if we're in deferred mode first, before extracting state
156
        if !self.is_deferred() {
350✔
157
            return Err(ReflectError::InvariantViolation {
2✔
158
                invariant: "finish_deferred() called but deferred mode is not enabled",
2✔
159
            });
2✔
160
        }
348✔
161

162
        // Extract deferred state, transitioning back to Strict mode
163
        let FrameMode::Deferred {
164
            stack,
348✔
165
            start_depth,
348✔
166
            mut stored_frames,
348✔
167
            ..
168
        } = core::mem::replace(&mut self.mode, FrameMode::Strict { stack: Vec::new() })
348✔
169
        else {
UNCOV
170
            unreachable!("just checked is_deferred()");
×
171
        };
172

173
        // Restore the stack to self.mode
174
        self.mode = FrameMode::Strict { stack };
348✔
175

176
        // Sort paths by depth (deepest first) so we process children before parents
177
        let mut paths: Vec<_> = stored_frames.keys().cloned().collect();
348✔
178
        paths.sort_by_key(|b| core::cmp::Reverse(b.len()));
1,594✔
179

180
        trace!(
181
            "finish_deferred: Processing {} stored frames in order: {:?}",
182
            paths.len(),
183
            paths
184
        );
185

186
        // Process each stored frame from deepest to shallowest
187
        for path in paths {
750✔
188
            let mut frame = stored_frames.remove(&path).unwrap();
750✔
189

190
            trace!(
191
                "finish_deferred: Processing frame at {:?}, shape {}, tracker {:?}",
192
                path,
193
                frame.shape,
194
                frame.tracker.kind()
195
            );
196

197
            // Fill in defaults for unset fields that have defaults
198
            if let Err(e) = frame.fill_defaults() {
750✔
199
                // Couldn't fill defaults (e.g., opaque field with #[facet(default)] but no default impl)
UNCOV
200
                frame.deinit();
×
UNCOV
201
                for (_, mut remaining_frame) in stored_frames {
×
UNCOV
202
                    remaining_frame.deinit();
×
UNCOV
203
                }
×
UNCOV
204
                return Err(e);
×
205
            }
750✔
206

207
            // Validate the frame is fully initialized
208
            if let Err(e) = frame.require_full_initialization() {
750✔
209
                // With the ownership transfer model:
210
                // - Parent's iset was cleared when we entered this field
211
                // - Parent won't drop it, so we must deinit it ourselves
212
                frame.deinit();
4✔
213

214
                // Clean up remaining stored frames before returning error.
215
                // All stored frames have their parent's iset cleared, so we must deinit them.
216
                // Note: we must call deinit() even for partially initialized frames, since
217
                // deinit() properly handles partial initialization via the tracker's iset.
218
                for (_, mut remaining_frame) in stored_frames {
4✔
219
                    remaining_frame.deinit();
3✔
220
                }
3✔
221

222
                // No need to poison - returning Err consumes self, Drop will handle cleanup
223
                return Err(e);
4✔
224
            }
746✔
225

226
            // Update parent's ISet to mark this field as initialized.
227
            // The parent could be:
228
            // 1. On the frames stack (if path.len() == 1, parent is at start_depth - 1)
229
            // 2. On the frames stack (if parent was pushed but never ended)
230
            // 3. In stored_frames (if parent was ended during deferred mode)
231
            if let Some(field_name) = path.last() {
746✔
232
                let parent_path: Vec<_> = path[..path.len() - 1].to_vec();
746✔
233

234
                if parent_path.is_empty() {
746✔
235
                    // Parent is the frame that was current when deferred mode started.
236
                    // It's at index (start_depth - 1) because deferred mode stores frames
237
                    // relative to the position at start_depth.
238
                    let parent_index = start_depth.saturating_sub(1);
486✔
239
                    if let Some(root_frame) = self.frames_mut().get_mut(parent_index) {
486✔
240
                        Self::mark_field_initialized(root_frame, field_name);
486✔
241
                    }
486✔
242
                } else {
243
                    // Try stored_frames first
244
                    if let Some(parent_frame) = stored_frames.get_mut(&parent_path) {
260✔
245
                        Self::mark_field_initialized(parent_frame, field_name);
260✔
246
                    } else {
260✔
247
                        // Parent might still be on the frames stack (never ended).
248
                        // The frame at index (start_depth + parent_path.len() - 1) should be the parent.
UNCOV
249
                        let parent_frame_index = start_depth + parent_path.len() - 1;
×
250
                        if let Some(parent_frame) = self.frames_mut().get_mut(parent_frame_index) {
×
251
                            Self::mark_field_initialized(parent_frame, field_name);
×
252
                        }
×
253
                    }
254
                }
UNCOV
255
            }
×
256

257
            // Frame is validated and parent is updated - frame is no longer needed
258
            // (The actual data is already in place in memory, pointed to by parent)
259
            drop(frame);
746✔
260
        }
261

262
        // Invariant check: we must have at least one frame after finish_deferred
263
        if self.frames().is_empty() {
344✔
264
            // No need to poison - returning Err consumes self, Drop will handle cleanup
UNCOV
265
            return Err(ReflectError::InvariantViolation {
×
266
                invariant: "finish_deferred() left Partial with no frames",
×
267
            });
×
268
        }
344✔
269

270
        // Fill defaults and validate the root frame is fully initialized
271
        if let Some(frame) = self.frames_mut().last_mut() {
344✔
272
            // Fill defaults - this can fail if a field has #[facet(default)] but no default impl
273
            frame.fill_defaults()?;
344✔
274
            // Root validation failed. At this point, all stored frames have been
275
            // processed and their parent isets updated.
276
            // No need to poison - returning Err consumes self, Drop will handle cleanup
277
            frame.require_full_initialization()?;
344✔
UNCOV
278
        }
×
279

280
        Ok(self)
340✔
281
    }
350✔
282

283
    /// Mark a field as initialized in a frame's tracker
284
    fn mark_field_initialized(frame: &mut Frame, field_name: &str) {
746✔
285
        if let Some(idx) = Self::find_field_index(frame, field_name) {
746✔
286
            // If the tracker is Scalar but this is a struct type, upgrade to Struct tracker.
287
            // This can happen if the frame was deinit'd (e.g., by a failed set_default)
288
            // which resets the tracker to Scalar.
289
            if matches!(frame.tracker, Tracker::Scalar) {
744✔
UNCOV
290
                if let Type::User(UserType::Struct(struct_type)) = frame.shape.ty {
×
291
                    frame.tracker = Tracker::Struct {
×
292
                        iset: ISet::new(struct_type.fields.len()),
×
293
                        current_child: None,
×
294
                    };
×
295
                }
×
296
            }
744✔
297

298
            match &mut frame.tracker {
744✔
299
                Tracker::Struct { iset, .. } => {
653✔
300
                    iset.set(idx);
653✔
301
                }
653✔
302
                Tracker::Enum { data, .. } => {
91✔
303
                    data.set(idx);
91✔
304
                }
91✔
UNCOV
305
                Tracker::Array { iset, .. } => {
×
306
                    iset.set(idx);
×
307
                }
×
308
                _ => {}
×
309
            }
310
        }
2✔
311
    }
746✔
312

313
    /// Find the field index for a given field name in a frame
314
    fn find_field_index(frame: &Frame, field_name: &str) -> Option<usize> {
746✔
315
        match frame.shape.ty {
746✔
316
            Type::User(UserType::Struct(struct_type)) => {
653✔
317
                struct_type.fields.iter().position(|f| f.name == field_name)
1,373✔
318
            }
319
            Type::User(UserType::Enum(_)) => {
320
                if let Tracker::Enum { variant, .. } = &frame.tracker {
93✔
321
                    variant
93✔
322
                        .data
93✔
323
                        .fields
93✔
324
                        .iter()
93✔
325
                        .position(|f| f.name == field_name)
112✔
326
                } else {
327
                    None
×
328
                }
329
            }
330
            _ => None,
×
331
        }
332
    }
746✔
333

334
    /// Pops the current frame off the stack, indicating we're done initializing the current field
335
    pub fn end(mut self) -> Result<Self, ReflectError> {
19,995✔
336
        if let Some(_frame) = self.frames().last() {
19,995✔
337
            crate::trace!(
19,995✔
338
                "end() called: shape={}, tracker={:?}, is_init={}",
19,995✔
339
                _frame.shape,
19,995✔
340
                _frame.tracker.kind(),
19,995✔
341
                _frame.is_init
19,995✔
342
            );
19,995✔
343
        }
19,995✔
344

345
        // Special handling for SmartPointerSlice - convert builder to Arc
346
        if self.frames().len() == 1 {
19,995✔
347
            let frames = self.frames_mut();
14✔
348
            if let Tracker::SmartPointerSlice {
349
                vtable,
13✔
350
                building_item,
13✔
351
            } = &frames[0].tracker
14✔
352
            {
353
                if *building_item {
13✔
UNCOV
354
                    return Err(ReflectError::OperationFailed {
×
UNCOV
355
                        shape: frames[0].shape,
×
UNCOV
356
                        operation: "still building an item, finish it first",
×
UNCOV
357
                    });
×
358
                }
13✔
359

360
                // Convert the builder to Arc<[T]>
361
                let vtable = *vtable;
13✔
362
                let builder_ptr = unsafe { frames[0].data.assume_init() };
13✔
363
                let arc_ptr = unsafe { (vtable.convert_fn)(builder_ptr) };
13✔
364

365
                // Update the frame to store the Arc
366
                frames[0].data = PtrUninit::new(unsafe {
13✔
367
                    NonNull::new_unchecked(arc_ptr.as_byte_ptr() as *mut u8)
13✔
368
                });
13✔
369
                frames[0].tracker = Tracker::Scalar;
13✔
370
                frames[0].is_init = true;
13✔
371
                // The builder memory has been consumed by convert_fn, so we no longer own it
372
                frames[0].ownership = FrameOwnership::ManagedElsewhere;
13✔
373

374
                return Ok(self);
13✔
375
            }
1✔
376
        }
19,981✔
377

378
        if self.frames().len() <= 1 {
19,982✔
379
            // Never pop the last/root frame - this indicates a broken state machine
380
            // No need to poison - returning Err consumes self, Drop will handle cleanup
381
            return Err(ReflectError::InvariantViolation {
1✔
382
                invariant: "Partial::end() called with only one frame on the stack",
1✔
383
            });
1✔
384
        }
19,981✔
385

386
        // In deferred mode, cannot pop below the start depth
387
        if let Some(start_depth) = self.start_depth() {
19,981✔
388
            if self.frames().len() <= start_depth {
1,221✔
389
                // No need to poison - returning Err consumes self, Drop will handle cleanup
390
                return Err(ReflectError::InvariantViolation {
×
391
                    invariant: "Partial::end() called but would pop below deferred start depth",
×
392
                });
×
393
            }
1,221✔
394
        }
18,760✔
395

396
        // Require that the top frame is fully initialized before popping.
397
        // Skip this check in deferred mode - validation happens in finish_deferred().
398
        // EXCEPT for collection items (map, list, set, option) which must be fully
399
        // initialized before insertion/completion.
400
        let requires_full_init = if !self.is_deferred() {
19,981✔
401
            true
18,760✔
402
        } else {
403
            // In deferred mode, check if parent is a collection that requires
404
            // fully initialized items (map, list, set, option)
405
            if self.frames().len() >= 2 {
1,221✔
406
                let frame_len = self.frames().len();
1,221✔
407
                let parent_frame = &self.frames()[frame_len - 2];
1,221✔
408
                matches!(
961✔
409
                    parent_frame.tracker,
35✔
410
                    Tracker::Map { .. }
411
                        | Tracker::List { .. }
412
                        | Tracker::Set { .. }
413
                        | Tracker::Option { .. }
414
                        | Tracker::Result { .. }
415
                        | Tracker::DynamicValue {
416
                            state: DynamicValueState::Array { .. }
417
                        }
418
                )
419
            } else {
UNCOV
420
                false
×
421
            }
422
        };
423

424
        if requires_full_init {
19,981✔
425
            let frame = self.frames().last().unwrap();
19,020✔
426
            crate::trace!(
427
                "end(): Checking full init for {}, tracker={:?}, is_init={}",
428
                frame.shape,
429
                frame.tracker.kind(),
430
                frame.is_init
431
            );
432
            let result = frame.require_full_initialization();
19,020✔
433
            crate::trace!(
434
                "end(): require_full_initialization result: {:?}",
435
                result.is_ok()
436
            );
437
            result?
19,020✔
438
        }
961✔
439

440
        // Pop the frame and save its data pointer for SmartPointer handling
441
        let mut popped_frame = self.frames_mut().pop().unwrap();
19,976✔
442

443
        // In deferred mode, store the frame for potential re-entry and skip
444
        // the normal parent-updating logic. The frame will be finalized later
445
        // in finish_deferred().
446
        //
447
        // We only store if the path depth matches the frame depth, meaning we're
448
        // ending a tracked struct/enum field, not something like begin_some()
449
        // or a field inside a collection item.
450
        if let FrameMode::Deferred {
451
            stack,
1,221✔
452
            start_depth,
1,221✔
453
            current_path,
1,221✔
454
            stored_frames,
1,221✔
455
            ..
456
        } = &mut self.mode
19,976✔
457
        {
458
            // Path depth should match the relative frame depth for a tracked field.
459
            // After popping: frames.len() - start_depth + 1 should equal path.len()
460
            // for fields entered via begin_field (not begin_some/begin_inner).
461
            let relative_depth = stack.len() - *start_depth + 1;
1,221✔
462
            let is_tracked_field = !current_path.is_empty() && current_path.len() == relative_depth;
1,221✔
463

464
            if is_tracked_field {
1,221✔
465
                trace!(
466
                    "end(): Storing frame for deferred path {:?}, shape {}",
467
                    current_path, popped_frame.shape
468
                );
469

470
                // Store the frame at the current path
471
                let path = current_path.clone();
833✔
472
                stored_frames.insert(path, popped_frame);
833✔
473

474
                // Pop from current_path
475
                current_path.pop();
833✔
476

477
                // Clear parent's current_child tracking
478
                if let Some(parent_frame) = stack.last_mut() {
833✔
479
                    parent_frame.tracker.clear_current_child();
833✔
480
                }
833✔
481

482
                return Ok(self);
833✔
483
            }
388✔
484
        }
18,755✔
485

486
        // check if this needs deserialization from a different shape
487
        if popped_frame.using_custom_deserialization {
19,143✔
488
            // First try field-level proxy
489
            let deserialize_with = self
62✔
490
                .parent_field()
62✔
491
                .and_then(|field| field.proxy_convert_in_fn());
62✔
492

493
            // Fall back to shape-level proxy stored in the frame
494
            #[cfg(feature = "alloc")]
495
            let deserialize_with =
62✔
496
                deserialize_with.or_else(|| popped_frame.shape_level_proxy.map(|p| p.convert_in));
62✔
497

498
            if let Some(deserialize_with) = deserialize_with {
62✔
499
                let parent_frame = self.frames_mut().last_mut().unwrap();
62✔
500

501
                trace!(
502
                    "Detected custom conversion needed from {} to {}",
503
                    popped_frame.shape, parent_frame.shape
504
                );
505

506
                unsafe {
507
                    let res = {
62✔
508
                        let inner_value_ptr = popped_frame.data.assume_init().as_const();
62✔
509
                        (deserialize_with)(inner_value_ptr, parent_frame.data)
62✔
510
                    };
511
                    let popped_frame_shape = popped_frame.shape;
62✔
512

513
                    // Note: We do NOT call deinit() here because deserialize_with uses
514
                    // ptr::read to take ownership of the source value. Calling deinit()
515
                    // would cause a double-free. We mark is_init as false to satisfy
516
                    // dealloc()'s assertion, then deallocate the memory.
517
                    popped_frame.is_init = false;
62✔
518
                    popped_frame.dealloc();
62✔
519
                    let rptr = res.map_err(|message| ReflectError::CustomDeserializationError {
62✔
520
                        message,
6✔
521
                        src_shape: popped_frame_shape,
6✔
522
                        dst_shape: parent_frame.shape,
6✔
523
                    })?;
6✔
524
                    if rptr.as_uninit() != parent_frame.data {
56✔
UNCOV
525
                        return Err(ReflectError::CustomDeserializationError {
×
UNCOV
526
                            message: "deserialize_with did not return the expected pointer".into(),
×
UNCOV
527
                            src_shape: popped_frame_shape,
×
UNCOV
528
                            dst_shape: parent_frame.shape,
×
UNCOV
529
                        });
×
530
                    }
56✔
531
                    parent_frame.mark_as_init();
56✔
532
                }
533
                return Ok(self);
56✔
UNCOV
534
            }
×
535
        }
19,081✔
536

537
        // Update parent frame's tracking when popping from a child
538
        let parent_frame = self.frames_mut().last_mut().unwrap();
19,081✔
539

540
        crate::trace!(
541
            "end(): Popped {} (tracker {:?}), Parent {} (tracker {:?})",
542
            popped_frame.shape,
543
            popped_frame.tracker.kind(),
544
            parent_frame.shape,
545
            parent_frame.tracker.kind()
546
        );
547

548
        // Check if we need to do a conversion - this happens when:
549
        // 1. The parent frame has an inner type that matches the popped frame's shape
550
        // 2. The parent frame has try_from
551
        // 3. The parent frame is not yet initialized
552
        // 4. The parent frame's tracker is Scalar (not Option, SmartPointer, etc.)
553
        //    This ensures we only do conversion when begin_inner was used, not begin_some
554
        let needs_conversion = !parent_frame.is_init
19,081✔
555
            && matches!(parent_frame.tracker, Tracker::Scalar)
3,930✔
556
            && parent_frame.shape.inner.is_some()
109✔
557
            && parent_frame.shape.inner.unwrap() == popped_frame.shape
109✔
558
            && parent_frame.shape.vtable.try_from.is_some();
101✔
559

560
        if needs_conversion {
19,081✔
561
            trace!(
562
                "Detected implicit conversion needed from {} to {}",
563
                popped_frame.shape, parent_frame.shape
564
            );
565

566
            // The conversion requires the source frame to be fully initialized
567
            // (we're about to call assume_init() and pass to try_from)
568
            if let Err(e) = popped_frame.require_full_initialization() {
101✔
569
                // Deallocate the memory since the frame wasn't fully initialized
UNCOV
570
                if let FrameOwnership::Owned = popped_frame.ownership {
×
UNCOV
571
                    if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
×
UNCOV
572
                        if layout.size() > 0 {
×
573
                            trace!(
574
                                "Deallocating uninitialized conversion frame memory: size={}, align={}",
575
                                layout.size(),
576
                                layout.align()
577
                            );
UNCOV
578
                            unsafe {
×
UNCOV
579
                                ::alloc::alloc::dealloc(
×
UNCOV
580
                                    popped_frame.data.as_mut_byte_ptr(),
×
581
                                    layout,
×
582
                                );
×
583
                            }
×
584
                        }
×
585
                    }
×
UNCOV
586
                }
×
UNCOV
587
                return Err(e);
×
588
            }
101✔
589

590
            // Perform the conversion
591
            if let Some(try_from_fn) = parent_frame.shape.vtable.try_from {
101✔
592
                let inner_ptr = unsafe { popped_frame.data.assume_init().as_const() };
101✔
593
                let inner_shape = popped_frame.shape;
101✔
594

595
                trace!("Converting from {} to {}", inner_shape, parent_frame.shape);
596
                let result = unsafe { try_from_fn(inner_ptr, inner_shape, parent_frame.data) };
101✔
597

598
                if let Err(e) = result {
101✔
599
                    trace!("Conversion failed: {e:?}");
600

601
                    // Deallocate the inner value's memory since conversion failed
602
                    if let FrameOwnership::Owned = popped_frame.ownership {
2✔
603
                        if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
2✔
604
                            if layout.size() > 0 {
2✔
605
                                trace!(
606
                                    "Deallocating conversion frame memory after failure: size={}, align={}",
607
                                    layout.size(),
608
                                    layout.align()
609
                                );
610
                                unsafe {
2✔
611
                                    ::alloc::alloc::dealloc(
2✔
612
                                        popped_frame.data.as_mut_byte_ptr(),
2✔
613
                                        layout,
2✔
614
                                    );
2✔
615
                                }
2✔
UNCOV
616
                            }
×
UNCOV
617
                        }
×
UNCOV
618
                    }
×
619

620
                    return Err(ReflectError::TryFromError {
2✔
621
                        src_shape: inner_shape,
2✔
622
                        dst_shape: parent_frame.shape,
2✔
623
                        inner: e,
2✔
624
                    });
2✔
625
                }
99✔
626

627
                trace!("Conversion succeeded, marking parent as initialized");
628
                parent_frame.is_init = true;
99✔
629

630
                // Deallocate the inner value's memory since try_from consumed it
631
                if let FrameOwnership::Owned = popped_frame.ownership {
99✔
632
                    if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
99✔
633
                        if layout.size() > 0 {
99✔
634
                            trace!(
635
                                "Deallocating conversion frame memory: size={}, align={}",
636
                                layout.size(),
637
                                layout.align()
638
                            );
639
                            unsafe {
99✔
640
                                ::alloc::alloc::dealloc(
99✔
641
                                    popped_frame.data.as_mut_byte_ptr(),
99✔
642
                                    layout,
99✔
643
                                );
99✔
644
                            }
99✔
UNCOV
645
                        }
×
UNCOV
646
                    }
×
UNCOV
647
                }
×
648

649
                return Ok(self);
99✔
UNCOV
650
            }
×
651
        }
18,980✔
652

653
        // For Field-owned frames, reclaim responsibility in parent's tracker
654
        // Only mark as initialized if the child frame was actually initialized.
655
        // This prevents double-free when begin_inner/begin_some drops a value via
656
        // prepare_for_reinitialization but then fails, leaving the child uninitialized.
657
        //
658
        // We use require_full_initialization() rather than just is_init because:
659
        // - Scalar frames use is_init as the source of truth
660
        // - Struct/Array/Enum frames use their iset/data as the source of truth
661
        //   (is_init may never be set to true for these tracker types)
662
        if let FrameOwnership::Field { field_idx } = popped_frame.ownership {
18,980✔
663
            let child_is_initialized = popped_frame.require_full_initialization().is_ok();
3,420✔
664
            match &mut parent_frame.tracker {
3,420✔
665
                Tracker::Struct {
666
                    iset,
2,971✔
667
                    current_child,
2,971✔
668
                } => {
669
                    if child_is_initialized {
2,971✔
670
                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
2,971✔
671
                    }
2,971✔
672
                    *current_child = None;
2,971✔
673
                }
674
                Tracker::Array {
675
                    iset,
145✔
676
                    current_child,
145✔
677
                } => {
678
                    if child_is_initialized {
145✔
679
                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
145✔
680
                    }
145✔
681
                    *current_child = None;
145✔
682
                }
683
                Tracker::Enum {
684
                    data,
304✔
685
                    current_child,
304✔
686
                    ..
687
                } => {
688
                    if child_is_initialized {
304✔
689
                        data.set(field_idx); // Parent reclaims responsibility only if child was init
304✔
690
                    }
304✔
691
                    *current_child = None;
304✔
692
                }
UNCOV
693
                _ => {}
×
694
            }
695
            return Ok(self);
3,420✔
696
        }
15,560✔
697

698
        match &mut parent_frame.tracker {
12,837✔
699
            Tracker::SmartPointer => {
700
                // We just popped the inner value frame, so now we need to create the smart pointer
701
                if let Def::Pointer(smart_ptr_def) = parent_frame.shape.def {
63✔
702
                    // The inner value must be fully initialized before we can create the smart pointer
703
                    if let Err(e) = popped_frame.require_full_initialization() {
63✔
704
                        // Inner value wasn't initialized, deallocate and return error
UNCOV
705
                        popped_frame.deinit();
×
706
                        popped_frame.dealloc();
×
UNCOV
707
                        return Err(e);
×
708
                    }
63✔
709

710
                    let Some(new_into_fn) = smart_ptr_def.vtable.new_into_fn else {
63✔
UNCOV
711
                        popped_frame.deinit();
×
UNCOV
712
                        popped_frame.dealloc();
×
UNCOV
713
                        return Err(ReflectError::OperationFailed {
×
UNCOV
714
                            shape: parent_frame.shape,
×
UNCOV
715
                            operation: "SmartPointer missing new_into_fn",
×
UNCOV
716
                        });
×
717
                    };
718

719
                    // The child frame contained the inner value
720
                    let inner_ptr = PtrMut::new(unsafe {
63✔
721
                        NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
63✔
722
                    });
723

724
                    // Use new_into_fn to create the Box
725
                    unsafe {
63✔
726
                        new_into_fn(parent_frame.data, inner_ptr);
63✔
727
                    }
63✔
728

729
                    // We just moved out of it
730
                    popped_frame.tracker = Tracker::Scalar;
63✔
731
                    popped_frame.is_init = false;
63✔
732

733
                    // Deallocate the inner value's memory since new_into_fn moved it
734
                    popped_frame.dealloc();
63✔
735

736
                    parent_frame.is_init = true;
63✔
UNCOV
737
                }
×
738
            }
739
            Tracker::List { current_child } if parent_frame.is_init => {
12,837✔
740
                if *current_child {
12,837✔
741
                    // We just popped an element frame, now push it to the list
742
                    if let Def::List(list_def) = parent_frame.shape.def {
12,837✔
743
                        let Some(push_fn) = list_def.vtable.push else {
12,837✔
UNCOV
744
                            return Err(ReflectError::OperationFailed {
×
UNCOV
745
                                shape: parent_frame.shape,
×
UNCOV
746
                                operation: "List missing push function",
×
UNCOV
747
                            });
×
748
                        };
749

750
                        // The child frame contained the element value
751
                        let element_ptr = PtrMut::new(unsafe {
12,837✔
752
                            NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
12,837✔
753
                        });
754

755
                        // Use push to add element to the list
756
                        unsafe {
12,837✔
757
                            push_fn(
12,837✔
758
                                PtrMut::new(NonNull::new_unchecked(
12,837✔
759
                                    parent_frame.data.as_mut_byte_ptr(),
12,837✔
760
                                )),
12,837✔
761
                                element_ptr,
12,837✔
762
                            );
12,837✔
763
                        }
12,837✔
764

765
                        // Push moved out of popped_frame
766
                        popped_frame.tracker = Tracker::Scalar;
12,837✔
767
                        popped_frame.is_init = false;
12,837✔
768
                        popped_frame.dealloc();
12,837✔
769

770
                        *current_child = false;
12,837✔
771
                    }
×
772
                }
×
773
            }
774
            Tracker::Map { insert_state } if parent_frame.is_init => {
2,224✔
775
                match insert_state {
2,224✔
776
                    MapInsertState::PushingKey { key_ptr, .. } => {
1,115✔
777
                        // We just popped the key frame - mark key as initialized and transition
1,115✔
778
                        // to PushingValue state
1,115✔
779
                        *insert_state = MapInsertState::PushingValue {
1,115✔
780
                            key_ptr: *key_ptr,
1,115✔
781
                            value_ptr: None,
1,115✔
782
                            value_initialized: false,
1,115✔
783
                        };
1,115✔
784
                    }
1,115✔
785
                    MapInsertState::PushingValue {
786
                        key_ptr, value_ptr, ..
1,109✔
787
                    } => {
788
                        // We just popped the value frame, now insert the pair
789
                        if let (Some(value_ptr), Def::Map(map_def)) =
1,109✔
790
                            (value_ptr, parent_frame.shape.def)
1,109✔
791
                        {
792
                            let insert_fn = map_def.vtable.insert_fn;
1,109✔
793

794
                            // Use insert to add key-value pair to the map
795
                            unsafe {
1,109✔
796
                                insert_fn(
1,109✔
797
                                    PtrMut::new(NonNull::new_unchecked(
1,109✔
798
                                        parent_frame.data.as_mut_byte_ptr(),
1,109✔
799
                                    )),
1,109✔
800
                                    PtrMut::new(NonNull::new_unchecked(key_ptr.as_mut_byte_ptr())),
1,109✔
801
                                    PtrMut::new(NonNull::new_unchecked(
1,109✔
802
                                        value_ptr.as_mut_byte_ptr(),
1,109✔
803
                                    )),
1,109✔
804
                                );
1,109✔
805
                            }
1,109✔
806

807
                            // Note: We don't deallocate the key and value memory here.
808
                            // The insert function has semantically moved the values into the map,
809
                            // but we still need to deallocate the temporary buffers.
810
                            // However, since we don't have frames for them anymore (they were popped),
811
                            // we need to handle deallocation here.
812
                            if let Ok(key_shape) = map_def.k().layout.sized_layout() {
1,109✔
813
                                if key_shape.size() > 0 {
1,109✔
814
                                    unsafe {
1,109✔
815
                                        ::alloc::alloc::dealloc(
1,109✔
816
                                            key_ptr.as_mut_byte_ptr(),
1,109✔
817
                                            key_shape,
1,109✔
818
                                        );
1,109✔
819
                                    }
1,109✔
UNCOV
820
                                }
×
UNCOV
821
                            }
×
822
                            if let Ok(value_shape) = map_def.v().layout.sized_layout() {
1,109✔
823
                                if value_shape.size() > 0 {
1,109✔
824
                                    unsafe {
1,109✔
825
                                        ::alloc::alloc::dealloc(
1,109✔
826
                                            value_ptr.as_mut_byte_ptr(),
1,109✔
827
                                            value_shape,
1,109✔
828
                                        );
1,109✔
829
                                    }
1,109✔
UNCOV
830
                                }
×
UNCOV
831
                            }
×
832

833
                            // Reset to idle state
834
                            *insert_state = MapInsertState::Idle;
1,109✔
UNCOV
835
                        }
×
836
                    }
UNCOV
837
                    MapInsertState::Idle => {
×
UNCOV
838
                        // Nothing to do
×
UNCOV
839
                    }
×
840
                }
841
            }
842
            Tracker::Set { current_child } if parent_frame.is_init => {
42✔
843
                if *current_child {
42✔
844
                    // We just popped an element frame, now insert it into the set
845
                    if let Def::Set(set_def) = parent_frame.shape.def {
42✔
846
                        let insert_fn = set_def.vtable.insert_fn;
42✔
847

42✔
848
                        // The child frame contained the element value
42✔
849
                        let element_ptr = PtrMut::new(unsafe {
42✔
850
                            NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
42✔
851
                        });
42✔
852

42✔
853
                        // Use insert to add element to the set
42✔
854
                        unsafe {
42✔
855
                            insert_fn(
42✔
856
                                PtrMut::new(NonNull::new_unchecked(
42✔
857
                                    parent_frame.data.as_mut_byte_ptr(),
42✔
858
                                )),
42✔
859
                                element_ptr,
42✔
860
                            );
42✔
861
                        }
42✔
862

42✔
863
                        // Insert moved out of popped_frame
42✔
864
                        popped_frame.tracker = Tracker::Scalar;
42✔
865
                        popped_frame.is_init = false;
42✔
866
                        popped_frame.dealloc();
42✔
867

42✔
868
                        *current_child = false;
42✔
869
                    }
42✔
UNCOV
870
                }
×
871
            }
872
            Tracker::Option { building_inner } => {
299✔
873
                crate::trace!(
874
                    "end(): matched Tracker::Option, building_inner={}",
875
                    *building_inner
876
                );
877
                // We just popped the inner value frame for an Option's Some variant
878
                if *building_inner {
299✔
879
                    if let Def::Option(option_def) = parent_frame.shape.def {
299✔
880
                        // Use the Option vtable to initialize Some(inner_value)
881
                        let init_some_fn = option_def.vtable.init_some_fn;
299✔
882

883
                        // The popped frame contains the inner value
884
                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
299✔
885

886
                        // Initialize the Option as Some(inner_value)
887
                        unsafe {
299✔
888
                            init_some_fn(parent_frame.data, inner_value_ptr);
299✔
889
                        }
299✔
890

891
                        // Deallocate the inner value's memory since init_some_fn moved it
892
                        if let FrameOwnership::Owned = popped_frame.ownership {
299✔
893
                            if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
299✔
894
                                if layout.size() > 0 {
299✔
895
                                    unsafe {
299✔
896
                                        ::alloc::alloc::dealloc(
299✔
897
                                            popped_frame.data.as_mut_byte_ptr(),
299✔
898
                                            layout,
299✔
899
                                        );
299✔
900
                                    }
299✔
UNCOV
901
                                }
×
UNCOV
902
                            }
×
UNCOV
903
                        }
×
904

905
                        // Mark that we're no longer building the inner value
906
                        *building_inner = false;
299✔
907
                        crate::trace!("end(): set building_inner to false");
908
                        // Mark the Option as initialized
909
                        parent_frame.is_init = true;
299✔
910
                        crate::trace!("end(): set parent_frame.is_init to true");
911
                    } else {
UNCOV
912
                        return Err(ReflectError::OperationFailed {
×
UNCOV
913
                            shape: parent_frame.shape,
×
UNCOV
914
                            operation: "Option frame without Option definition",
×
UNCOV
915
                        });
×
916
                    }
917
                } else {
918
                    // building_inner is false - the Option was already initialized but
919
                    // begin_some was called again. The popped frame was not used to
920
                    // initialize the Option, so we need to clean it up.
UNCOV
921
                    popped_frame.deinit();
×
UNCOV
922
                    if let FrameOwnership::Owned = popped_frame.ownership {
×
UNCOV
923
                        if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
×
UNCOV
924
                            if layout.size() > 0 {
×
UNCOV
925
                                unsafe {
×
926
                                    ::alloc::alloc::dealloc(
×
UNCOV
927
                                        popped_frame.data.as_mut_byte_ptr(),
×
UNCOV
928
                                        layout,
×
UNCOV
929
                                    );
×
UNCOV
930
                                }
×
UNCOV
931
                            }
×
UNCOV
932
                        }
×
UNCOV
933
                    }
×
934
                }
935
            }
936
            Tracker::Result {
937
                is_ok,
6✔
938
                building_inner,
6✔
939
            } => {
940
                crate::trace!(
941
                    "end(): matched Tracker::Result, is_ok={}, building_inner={}",
942
                    *is_ok,
943
                    *building_inner
944
                );
945
                // We just popped the inner value frame for a Result's Ok or Err variant
946
                if *building_inner {
6✔
947
                    if let Def::Result(result_def) = parent_frame.shape.def {
6✔
948
                        // The popped frame contains the inner value
949
                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
6✔
950

951
                        // Initialize the Result as Ok(inner_value) or Err(inner_value)
952
                        if *is_ok {
6✔
953
                            let init_ok_fn = result_def.vtable.init_ok_fn;
3✔
954
                            unsafe {
3✔
955
                                init_ok_fn(parent_frame.data, inner_value_ptr);
3✔
956
                            }
3✔
957
                        } else {
958
                            let init_err_fn = result_def.vtable.init_err_fn;
3✔
959
                            unsafe {
3✔
960
                                init_err_fn(parent_frame.data, inner_value_ptr);
3✔
961
                            }
3✔
962
                        }
963

964
                        // Deallocate the inner value's memory since init_ok/err_fn moved it
965
                        if let FrameOwnership::Owned = popped_frame.ownership {
6✔
966
                            if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
6✔
967
                                if layout.size() > 0 {
6✔
968
                                    unsafe {
6✔
969
                                        ::alloc::alloc::dealloc(
6✔
970
                                            popped_frame.data.as_mut_byte_ptr(),
6✔
971
                                            layout,
6✔
972
                                        );
6✔
973
                                    }
6✔
UNCOV
974
                                }
×
UNCOV
975
                            }
×
UNCOV
976
                        }
×
977

978
                        // Mark that we're no longer building the inner value
979
                        *building_inner = false;
6✔
980
                        crate::trace!("end(): set building_inner to false");
981
                        // Mark the Result as initialized
982
                        parent_frame.is_init = true;
6✔
983
                        crate::trace!("end(): set parent_frame.is_init to true");
984
                    } else {
985
                        return Err(ReflectError::OperationFailed {
×
986
                            shape: parent_frame.shape,
×
987
                            operation: "Result frame without Result definition",
×
988
                        });
×
989
                    }
990
                } else {
991
                    // building_inner is false - the Result was already initialized but
992
                    // begin_ok/begin_err was called again. The popped frame was not used to
993
                    // initialize the Result, so we need to clean it up.
UNCOV
994
                    popped_frame.deinit();
×
UNCOV
995
                    if let FrameOwnership::Owned = popped_frame.ownership {
×
UNCOV
996
                        if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
×
UNCOV
997
                            if layout.size() > 0 {
×
UNCOV
998
                                unsafe {
×
UNCOV
999
                                    ::alloc::alloc::dealloc(
×
UNCOV
1000
                                        popped_frame.data.as_mut_byte_ptr(),
×
UNCOV
1001
                                        layout,
×
UNCOV
1002
                                    );
×
UNCOV
1003
                                }
×
UNCOV
1004
                            }
×
UNCOV
1005
                        }
×
UNCOV
1006
                    }
×
1007
                }
1008
            }
1009
            Tracker::Scalar => {
1010
                // the main case here is: the popped frame was a `String` and the
1011
                // parent frame is an `Arc<str>`, `Box<str>` etc.
1012
                match &parent_frame.shape.def {
8✔
1013
                    Def::Pointer(smart_ptr_def) => {
8✔
1014
                        let pointee =
8✔
1015
                            smart_ptr_def
8✔
1016
                                .pointee()
8✔
1017
                                .ok_or(ReflectError::InvariantViolation {
8✔
1018
                                    invariant: "pointer type doesn't have a pointee",
8✔
1019
                                })?;
8✔
1020

1021
                        if !pointee.is_shape(str::SHAPE) {
8✔
UNCOV
1022
                            return Err(ReflectError::InvariantViolation {
×
UNCOV
1023
                                invariant: "only T=str is supported when building SmartPointer<T> and T is unsized",
×
UNCOV
1024
                            });
×
1025
                        }
8✔
1026

1027
                        if !popped_frame.shape.is_shape(String::SHAPE) {
8✔
UNCOV
1028
                            return Err(ReflectError::InvariantViolation {
×
UNCOV
1029
                                invariant: "the popped frame should be String when building a SmartPointer<T>",
×
1030
                            });
×
1031
                        }
8✔
1032

1033
                        popped_frame.require_full_initialization()?;
8✔
1034

1035
                        // if the just-popped frame was a SmartPointerStr, we have some conversion to do:
1036
                        // Special-case: SmartPointer<str> (Box<str>, Arc<str>, Rc<str>) via SmartPointerStr tracker
1037
                        // Here, popped_frame actually contains a value for String that should be moved into the smart pointer.
1038
                        // We convert the String into Box<str>, Arc<str>, or Rc<str> as appropriate and write it to the parent frame.
1039
                        use ::alloc::{rc::Rc, string::String, sync::Arc};
1040
                        let parent_shape = parent_frame.shape;
8✔
1041

1042
                        let Some(known) = smart_ptr_def.known else {
8✔
1043
                            return Err(ReflectError::OperationFailed {
×
1044
                                shape: parent_shape,
×
UNCOV
1045
                                operation: "SmartPointerStr for unknown smart pointer kind",
×
UNCOV
1046
                            });
×
1047
                        };
1048

1049
                        parent_frame.deinit();
8✔
1050

1051
                        // Interpret the memory as a String, then convert and write.
1052
                        let string_ptr = popped_frame.data.as_mut_byte_ptr() as *mut String;
8✔
1053
                        let string_value = unsafe { core::ptr::read(string_ptr) };
8✔
1054

1055
                        match known {
8✔
1056
                            KnownPointer::Box => {
1057
                                let boxed: Box<str> = string_value.into_boxed_str();
2✔
1058
                                unsafe {
2✔
1059
                                    core::ptr::write(
2✔
1060
                                        parent_frame.data.as_mut_byte_ptr() as *mut Box<str>,
2✔
1061
                                        boxed,
2✔
1062
                                    );
2✔
1063
                                }
2✔
1064
                            }
1065
                            KnownPointer::Arc => {
1066
                                let arc: Arc<str> = Arc::from(string_value.into_boxed_str());
2✔
1067
                                unsafe {
2✔
1068
                                    core::ptr::write(
2✔
1069
                                        parent_frame.data.as_mut_byte_ptr() as *mut Arc<str>,
2✔
1070
                                        arc,
2✔
1071
                                    );
2✔
1072
                                }
2✔
1073
                            }
1074
                            KnownPointer::Rc => {
1075
                                let rc: Rc<str> = Rc::from(string_value.into_boxed_str());
4✔
1076
                                unsafe {
4✔
1077
                                    core::ptr::write(
4✔
1078
                                        parent_frame.data.as_mut_byte_ptr() as *mut Rc<str>,
4✔
1079
                                        rc,
4✔
1080
                                    );
4✔
1081
                                }
4✔
1082
                            }
1083
                            _ => {
1084
                                return Err(ReflectError::OperationFailed {
×
1085
                                    shape: parent_shape,
×
1086
                                    operation: "Don't know how to build this pointer type",
×
UNCOV
1087
                                });
×
1088
                            }
1089
                        }
1090

1091
                        parent_frame.is_init = true;
8✔
1092

1093
                        popped_frame.tracker = Tracker::Scalar;
8✔
1094
                        popped_frame.is_init = false;
8✔
1095
                        popped_frame.dealloc();
8✔
1096
                    }
1097
                    _ => {
1098
                        // This can happen if begin_inner() was called on a type that
1099
                        // has shape.inner but isn't a SmartPointer (e.g., Option).
1100
                        // In this case, we can't complete the conversion, so return error.
1101
                        return Err(ReflectError::OperationFailed {
×
1102
                            shape: parent_frame.shape,
×
UNCOV
1103
                            operation: "end() called but parent has Uninit/Init tracker and isn't a SmartPointer",
×
UNCOV
1104
                        });
×
1105
                    }
1106
                }
1107
            }
1108
            Tracker::SmartPointerSlice {
1109
                vtable,
33✔
1110
                building_item,
33✔
1111
            } => {
1112
                if *building_item {
33✔
1113
                    // We just popped an element frame, now push it to the slice builder
1114
                    let element_ptr = PtrMut::new(unsafe {
33✔
1115
                        NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
33✔
1116
                    });
1117

1118
                    // Use the slice builder's push_fn to add the element
1119
                    crate::trace!("Pushing element to slice builder");
1120
                    unsafe {
33✔
1121
                        let parent_ptr = parent_frame.data.assume_init();
33✔
1122
                        (vtable.push_fn)(parent_ptr, element_ptr);
33✔
1123
                    }
33✔
1124

1125
                    popped_frame.tracker = Tracker::Scalar;
33✔
1126
                    popped_frame.is_init = false;
33✔
1127
                    popped_frame.dealloc();
33✔
1128

1129
                    if let Tracker::SmartPointerSlice {
1130
                        building_item: bi, ..
33✔
1131
                    } = &mut parent_frame.tracker
33✔
1132
                    {
33✔
1133
                        *bi = false;
33✔
1134
                    }
33✔
UNCOV
1135
                }
×
1136
            }
1137
            Tracker::DynamicValue {
1138
                state: DynamicValueState::Array { building_element },
22✔
1139
            } => {
1140
                if *building_element {
22✔
1141
                    // Check that the element is initialized before pushing
1142
                    if !popped_frame.is_init {
22✔
1143
                        // Element was never set - clean up and return error
UNCOV
1144
                        let shape = parent_frame.shape;
×
UNCOV
1145
                        popped_frame.dealloc();
×
UNCOV
1146
                        *building_element = false;
×
1147
                        // No need to poison - returning Err consumes self, Drop will handle cleanup
UNCOV
1148
                        return Err(ReflectError::OperationFailed {
×
UNCOV
1149
                            shape,
×
UNCOV
1150
                            operation: "end() called but array element was never initialized",
×
UNCOV
1151
                        });
×
1152
                    }
22✔
1153

1154
                    // We just popped an element frame, now push it to the dynamic array
1155
                    if let Def::DynamicValue(dyn_def) = parent_frame.shape.def {
22✔
1156
                        // Get mutable pointers - both array and element need PtrMut
22✔
1157
                        let array_ptr = unsafe { parent_frame.data.assume_init() };
22✔
1158
                        let element_ptr = unsafe { popped_frame.data.assume_init() };
22✔
1159

22✔
1160
                        // Use push_array_element to add element to the array
22✔
1161
                        unsafe {
22✔
1162
                            (dyn_def.vtable.push_array_element)(array_ptr, element_ptr);
22✔
1163
                        }
22✔
1164

22✔
1165
                        // Push moved out of popped_frame
22✔
1166
                        popped_frame.tracker = Tracker::Scalar;
22✔
1167
                        popped_frame.is_init = false;
22✔
1168
                        popped_frame.dealloc();
22✔
1169

22✔
1170
                        *building_element = false;
22✔
1171
                    }
22✔
UNCOV
1172
                }
×
1173
            }
1174
            Tracker::DynamicValue {
1175
                state: DynamicValueState::Object { insert_state },
26✔
1176
            } => {
1177
                if let DynamicObjectInsertState::BuildingValue { key } = insert_state {
26✔
1178
                    // Check that the value is initialized before inserting
1179
                    if !popped_frame.is_init {
26✔
1180
                        // Value was never set - clean up and return error
UNCOV
1181
                        let shape = parent_frame.shape;
×
UNCOV
1182
                        popped_frame.dealloc();
×
UNCOV
1183
                        *insert_state = DynamicObjectInsertState::Idle;
×
1184
                        // No need to poison - returning Err consumes self, Drop will handle cleanup
UNCOV
1185
                        return Err(ReflectError::OperationFailed {
×
UNCOV
1186
                            shape,
×
UNCOV
1187
                            operation: "end() called but object entry value was never initialized",
×
UNCOV
1188
                        });
×
1189
                    }
26✔
1190

1191
                    // We just popped a value frame, now insert it into the dynamic object
1192
                    if let Def::DynamicValue(dyn_def) = parent_frame.shape.def {
26✔
1193
                        // Get mutable pointers - both object and value need PtrMut
26✔
1194
                        let object_ptr = unsafe { parent_frame.data.assume_init() };
26✔
1195
                        let value_ptr = unsafe { popped_frame.data.assume_init() };
26✔
1196

26✔
1197
                        // Use insert_object_entry to add the key-value pair
26✔
1198
                        unsafe {
26✔
1199
                            (dyn_def.vtable.insert_object_entry)(object_ptr, key, value_ptr);
26✔
1200
                        }
26✔
1201

26✔
1202
                        // Insert moved out of popped_frame
26✔
1203
                        popped_frame.tracker = Tracker::Scalar;
26✔
1204
                        popped_frame.is_init = false;
26✔
1205
                        popped_frame.dealloc();
26✔
1206

26✔
1207
                        // Reset insert state to Idle
26✔
1208
                        *insert_state = DynamicObjectInsertState::Idle;
26✔
1209
                    }
26✔
UNCOV
1210
                }
×
1211
            }
UNCOV
1212
            _ => {}
×
1213
        }
1214

1215
        Ok(self)
15,560✔
1216
    }
19,995✔
1217

1218
    /// Returns a human-readable path representing the current traversal in the builder,
1219
    /// e.g., `RootStruct.fieldName[index].subfield`.
1220
    pub fn path(&self) -> String {
3,165✔
1221
        let mut out = String::new();
3,165✔
1222

1223
        let mut path_components = Vec::new();
3,165✔
1224
        // The stack of enum/struct/sequence names currently in context.
1225
        // Start from root and build upwards.
1226
        for (i, frame) in self.frames().iter().enumerate() {
6,616✔
1227
            match frame.shape.ty {
6,616✔
1228
                Type::User(user_type) => match user_type {
5,688✔
1229
                    UserType::Struct(struct_type) => {
3,379✔
1230
                        // Try to get currently active field index
1231
                        let mut field_str = None;
3,379✔
1232
                        if let Tracker::Struct {
1233
                            current_child: Some(idx),
2,212✔
1234
                            ..
1235
                        } = &frame.tracker
2,605✔
1236
                        {
1237
                            if let Some(field) = struct_type.fields.get(*idx) {
2,212✔
1238
                                field_str = Some(field.name);
2,212✔
1239
                            }
2,212✔
1240
                        }
1,167✔
1241
                        if i == 0 {
3,379✔
1242
                            // Use Display for the root struct shape
2,773✔
1243
                            path_components.push(format!("{}", frame.shape));
2,773✔
1244
                        }
2,773✔
1245
                        if let Some(field_name) = field_str {
3,379✔
1246
                            path_components.push(format!(".{field_name}"));
2,212✔
1247
                        }
2,212✔
1248
                    }
1249
                    UserType::Enum(_enum_type) => {
500✔
1250
                        // Try to get currently active variant and field
1251
                        if let Tracker::Enum {
1252
                            variant,
295✔
1253
                            current_child,
295✔
1254
                            ..
1255
                        } = &frame.tracker
500✔
1256
                        {
1257
                            if i == 0 {
295✔
1258
                                // Use Display for the root enum shape
100✔
1259
                                path_components.push(format!("{}", frame.shape));
100✔
1260
                            }
195✔
1261
                            path_components.push(format!("::{}", variant.name));
295✔
1262
                            if let Some(idx) = *current_child {
295✔
1263
                                if let Some(field) = variant.data.fields.get(idx) {
187✔
1264
                                    path_components.push(format!(".{}", field.name));
187✔
1265
                                }
187✔
1266
                            }
108✔
1267
                        } else if i == 0 {
205✔
1268
                            // just the enum display
44✔
1269
                            path_components.push(format!("{}", frame.shape));
44✔
1270
                        }
161✔
1271
                    }
UNCOV
1272
                    UserType::Union(_union_type) => {
×
UNCOV
1273
                        path_components.push(format!("{}", frame.shape));
×
UNCOV
1274
                    }
×
1275
                    UserType::Opaque => {
1,809✔
1276
                        path_components.push("<opaque>".to_string());
1,809✔
1277
                    }
1,809✔
1278
                },
1279
                Type::Sequence(seq_type) => match seq_type {
29✔
1280
                    facet_core::SequenceType::Array(_array_def) => {
29✔
1281
                        // Try to show current element index
1282
                        if let Tracker::Array {
1283
                            current_child: Some(idx),
19✔
1284
                            ..
1285
                        } = &frame.tracker
26✔
1286
                        {
19✔
1287
                            path_components.push(format!("[{idx}]"));
19✔
1288
                        }
19✔
1289
                    }
1290
                    // You can add more for Slice, Vec, etc., if applicable
UNCOV
1291
                    _ => {
×
UNCOV
1292
                        // just indicate "[]" for sequence
×
UNCOV
1293
                        path_components.push("[]".to_string());
×
UNCOV
1294
                    }
×
1295
                },
UNCOV
1296
                Type::Pointer(_) => {
×
UNCOV
1297
                    // Indicate deref
×
UNCOV
1298
                    path_components.push("*".to_string());
×
UNCOV
1299
                }
×
1300
                _ => {
899✔
1301
                    // No structural path
899✔
1302
                }
899✔
1303
            }
1304
        }
1305
        // Merge the path_components into a single string
1306
        for component in path_components {
7,439✔
1307
            out.push_str(&component);
7,439✔
1308
        }
7,439✔
1309
        out
3,165✔
1310
    }
3,165✔
1311

1312
    /// Get the field for the parent frame
1313
    pub fn parent_field(&self) -> Option<&Field> {
235✔
1314
        self.frames()
235✔
1315
            .iter()
235✔
1316
            .rev()
235✔
1317
            .nth(1)
235✔
1318
            .and_then(|f| f.get_field())
235✔
1319
    }
235✔
1320

1321
    /// Gets the field for the current frame
UNCOV
1322
    pub fn current_field(&self) -> Option<&Field> {
×
UNCOV
1323
        self.frames().last().and_then(|f| f.get_field())
×
UNCOV
1324
    }
×
1325
}
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