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

facet-rs / facet / 20025893534

08 Dec 2025 11:03AM UTC coverage: 58.703% (+0.08%) from 58.623%
20025893534

Pull #1176

github

web-flow
Merge 0af4cc5ba into e52f88d20
Pull Request #1176: fix: support untagged enums and flatten with externally-tagged enums

153 of 211 new or added lines in 9 files covered. (72.51%)

1 existing line in 1 file now uncovered.

24869 of 42364 relevant lines covered (58.7%)

543.51 hits per line

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

75.09
/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,324✔
37
        if self.state != PartialState::Active {
35,324✔
38
            panic!(
×
39
                "Partial::shape() called on non-active Partial (state: {:?})",
40
                self.state
41
            );
42
        }
35,324✔
43
        self.frames()
35,324✔
44
            .last()
35,324✔
45
            .expect("Partial::shape() called but no frames exist - this is a bug")
35,324✔
46
            .shape
35,324✔
47
    }
35,324✔
48

49
    /// Returns the shape of the current frame, or `None` if the Partial is
50
    /// inactive (poisoned or built) or has no frames.
51
    ///
52
    /// This is useful for debugging/logging where you want to inspect the state
53
    /// without risking a panic.
54
    #[inline]
55
    pub fn try_shape(&self) -> Option<&'static Shape> {
×
56
        if self.state != PartialState::Active {
×
57
            return None;
×
58
        }
×
59
        self.frames().last().map(|f| f.shape)
×
60
    }
×
61

62
    /// Returns the current deferred resolution, if in deferred mode.
63
    #[inline]
64
    pub fn deferred_resolution(&self) -> Option<&Resolution> {
3✔
65
        self.resolution()
3✔
66
    }
3✔
67

68
    /// Returns the current path in deferred mode as a slice (for debugging/tracing).
69
    #[inline]
70
    pub fn current_path_slice(&self) -> Option<&[&'static str]> {
×
71
        self.current_path().map(|p| p.as_slice())
×
72
    }
×
73

74
    /// Enables deferred materialization mode with the given Resolution.
75
    ///
76
    /// When deferred mode is enabled:
77
    /// - `end()` stores frames instead of validating them
78
    /// - Re-entering a path restores the stored frame with its state intact
79
    /// - `finish_deferred()` performs final validation and materialization
80
    ///
81
    /// This allows deserializers to handle interleaved fields (e.g., TOML dotted
82
    /// keys, flattened structs) where nested fields aren't contiguous in the input.
83
    ///
84
    /// # Use Cases
85
    ///
86
    /// - TOML dotted keys: `inner.x = 1` followed by `count = 2` then `inner.y = 3`
87
    /// - Flattened structs where nested fields appear at the parent level
88
    /// - Any format where field order doesn't match struct nesting
89
    ///
90
    /// # Errors
91
    ///
92
    /// Returns an error if already in deferred mode.
93
    #[inline]
94
    pub fn begin_deferred(mut self, resolution: Resolution) -> Result<Self, ReflectError> {
434✔
95
        // Cannot enable deferred mode if already in deferred mode
96
        if self.is_deferred() {
434✔
97
            return Err(ReflectError::InvariantViolation {
1✔
98
                invariant: "begin_deferred() called but already in deferred mode",
1✔
99
            });
1✔
100
        }
433✔
101

102
        // Take the stack out of Strict mode and wrap in Deferred mode
103
        let FrameMode::Strict { stack } = core::mem::replace(
433✔
104
            &mut self.mode,
433✔
105
            FrameMode::Strict { stack: Vec::new() }, // temporary placeholder
433✔
106
        ) else {
433✔
107
            unreachable!("just checked we're not in deferred mode");
×
108
        };
109

110
        let start_depth = stack.len();
433✔
111
        self.mode = FrameMode::Deferred {
433✔
112
            stack,
433✔
113
            resolution,
433✔
114
            start_depth,
433✔
115
            current_path: Vec::new(),
433✔
116
            stored_frames: BTreeMap::new(),
433✔
117
        };
433✔
118
        Ok(self)
433✔
119
    }
434✔
120

121
    /// Finishes deferred mode: validates all stored frames and finalizes.
122
    ///
123
    /// This method:
124
    /// 1. Validates that all stored frames are fully initialized
125
    /// 2. Processes frames from deepest to shallowest, updating parent ISets
126
    /// 3. Validates the root frame
127
    ///
128
    /// # Errors
129
    ///
130
    /// Returns an error if any required fields are missing or if the partial is
131
    /// not in deferred mode.
132
    pub fn finish_deferred(mut self) -> Result<Self, ReflectError> {
352✔
133
        // Check if we're in deferred mode first, before extracting state
134
        if !self.is_deferred() {
352✔
135
            return Err(ReflectError::InvariantViolation {
2✔
136
                invariant: "finish_deferred() called but deferred mode is not enabled",
2✔
137
            });
2✔
138
        }
350✔
139

140
        // Extract deferred state, transitioning back to Strict mode
141
        let FrameMode::Deferred {
142
            stack,
350✔
143
            start_depth,
350✔
144
            mut stored_frames,
350✔
145
            ..
146
        } = core::mem::replace(&mut self.mode, FrameMode::Strict { stack: Vec::new() })
350✔
147
        else {
148
            unreachable!("just checked is_deferred()");
×
149
        };
150

151
        // Restore the stack to self.mode
152
        self.mode = FrameMode::Strict { stack };
350✔
153

154
        // Sort paths by depth (deepest first) so we process children before parents
155
        let mut paths: Vec<_> = stored_frames.keys().cloned().collect();
350✔
156
        paths.sort_by_key(|b| core::cmp::Reverse(b.len()));
2,128✔
157

158
        trace!(
159
            "finish_deferred: Processing {} stored frames in order: {:?}",
160
            paths.len(),
161
            paths
162
        );
163

164
        // Process each stored frame from deepest to shallowest
165
        for path in paths {
846✔
166
            let mut frame = stored_frames.remove(&path).unwrap();
846✔
167

168
            trace!(
169
                "finish_deferred: Processing frame at {:?}, shape {}, tracker {:?}",
170
                path,
171
                frame.shape,
172
                frame.tracker.kind()
173
            );
174

175
            // Fill in defaults for unset fields that have defaults
176
            if let Err(e) = frame.fill_defaults() {
846✔
177
                // Couldn't fill defaults (e.g., opaque field with #[facet(default)] but no default impl)
178
                frame.deinit();
×
179
                for (_, mut remaining_frame) in stored_frames {
×
180
                    remaining_frame.deinit();
×
181
                }
×
182
                return Err(e);
×
183
            }
846✔
184

185
            // Validate the frame is fully initialized
186
            if let Err(e) = frame.require_full_initialization() {
846✔
187
                // With the ownership transfer model:
188
                // - Parent's iset was cleared when we entered this field
189
                // - Parent won't drop it, so we must deinit it ourselves
190
                frame.deinit();
4✔
191

192
                // Clean up remaining stored frames before returning error.
193
                // All stored frames have their parent's iset cleared, so we must deinit them.
194
                // Note: we must call deinit() even for partially initialized frames, since
195
                // deinit() properly handles partial initialization via the tracker's iset.
196
                for (_, mut remaining_frame) in stored_frames {
4✔
197
                    remaining_frame.deinit();
3✔
198
                }
3✔
199

200
                // No need to poison - returning Err consumes self, Drop will handle cleanup
201
                return Err(e);
4✔
202
            }
842✔
203

204
            // Update parent's ISet to mark this field as initialized.
205
            // The parent could be:
206
            // 1. On the frames stack (if path.len() == 1, parent is at start_depth - 1)
207
            // 2. On the frames stack (if parent was pushed but never ended)
208
            // 3. In stored_frames (if parent was ended during deferred mode)
209
            if let Some(field_name) = path.last() {
842✔
210
                let parent_path: Vec<_> = path[..path.len() - 1].to_vec();
842✔
211

212
                // Special handling for Option inner values: when path ends with "Some",
213
                // the parent is an Option frame and we need to complete the Option by
214
                // writing the inner value into the Option's memory.
215
                if *field_name == "Some" {
842✔
216
                    // Find the Option frame (parent)
217
                    let option_frame = if parent_path.is_empty() {
67✔
NEW
218
                        let parent_index = start_depth.saturating_sub(1);
×
NEW
219
                        self.frames_mut().get_mut(parent_index)
×
220
                    } else if let Some(parent_frame) = stored_frames.get_mut(&parent_path) {
67✔
221
                        Some(parent_frame)
67✔
222
                    } else {
NEW
223
                        let parent_frame_index = start_depth + parent_path.len() - 1;
×
NEW
224
                        self.frames_mut().get_mut(parent_frame_index)
×
225
                    };
226

227
                    if let Some(option_frame) = option_frame {
67✔
228
                        // The frame contains the inner value - write it into the Option's memory
229
                        Self::complete_option_frame(option_frame, frame);
67✔
230
                        // Frame data has been transferred to Option - don't drop it
231
                        continue;
67✔
NEW
232
                    }
×
233
                }
775✔
234

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

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

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

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

281
        Ok(self)
342✔
282
    }
352✔
283

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

299
            match &mut frame.tracker {
773✔
300
                Tracker::Struct { iset, .. } => {
678✔
301
                    iset.set(idx);
678✔
302
                }
678✔
303
                Tracker::Enum { data, .. } => {
95✔
304
                    data.set(idx);
95✔
305
                }
95✔
306
                Tracker::Array { iset, .. } => {
×
307
                    iset.set(idx);
×
308
                }
×
309
                _ => {}
×
310
            }
311
        }
2✔
312
    }
775✔
313

314
    /// Complete an Option frame by writing the inner value and marking it initialized.
315
    /// Used in finish_deferred when processing a stored frame at a path ending with "Some".
316
    fn complete_option_frame(option_frame: &mut Frame, inner_frame: Frame) {
67✔
317
        if let Def::Option(option_def) = option_frame.shape.def {
67✔
318
            // Use the Option vtable to initialize Some(inner_value)
319
            let init_some_fn = option_def.vtable.init_some_fn;
67✔
320

321
            // The inner frame contains the inner value
322
            let inner_value_ptr = unsafe { inner_frame.data.assume_init().as_const() };
67✔
323

324
            // Initialize the Option as Some(inner_value)
325
            unsafe {
67✔
326
                init_some_fn(option_frame.data, inner_value_ptr);
67✔
327
            }
67✔
328

329
            // Deallocate the inner value's memory since init_some_fn moved it
330
            if let FrameOwnership::Owned = inner_frame.ownership {
67✔
331
                if let Ok(layout) = inner_frame.shape.layout.sized_layout() {
67✔
332
                    if layout.size() > 0 {
67✔
333
                        unsafe {
67✔
334
                            ::alloc::alloc::dealloc(inner_frame.data.as_mut_byte_ptr(), layout);
67✔
335
                        }
67✔
NEW
336
                    }
×
NEW
337
                }
×
NEW
338
            }
×
339

340
            // Mark the Option as initialized
341
            option_frame.tracker = Tracker::Option {
67✔
342
                building_inner: false,
67✔
343
            };
67✔
344
            option_frame.is_init = true;
67✔
NEW
345
        }
×
346
    }
67✔
347

348
    /// Find the field index for a given field name in a frame
349
    fn find_field_index(frame: &Frame, field_name: &str) -> Option<usize> {
775✔
350
        match frame.shape.ty {
775✔
351
            Type::User(UserType::Struct(struct_type)) => {
678✔
352
                struct_type.fields.iter().position(|f| f.name == field_name)
1,406✔
353
            }
354
            Type::User(UserType::Enum(_)) => {
355
                if let Tracker::Enum { variant, .. } = &frame.tracker {
97✔
356
                    variant
97✔
357
                        .data
97✔
358
                        .fields
97✔
359
                        .iter()
97✔
360
                        .position(|f| f.name == field_name)
116✔
361
                } else {
362
                    None
×
363
                }
364
            }
365
            _ => None,
×
366
        }
367
    }
775✔
368

369
    /// Pops the current frame off the stack, indicating we're done initializing the current field
370
    pub fn end(mut self) -> Result<Self, ReflectError> {
20,005✔
371
        if let Some(_frame) = self.frames().last() {
20,005✔
372
            crate::trace!(
20,005✔
373
                "end() called: shape={}, tracker={:?}, is_init={}",
20,005✔
374
                _frame.shape,
20,005✔
375
                _frame.tracker.kind(),
20,005✔
376
                _frame.is_init
20,005✔
377
            );
20,005✔
378
        }
20,005✔
379

380
        // Special handling for SmartPointerSlice - convert builder to Arc
381
        if self.frames().len() == 1 {
20,005✔
382
            let frames = self.frames_mut();
14✔
383
            if let Tracker::SmartPointerSlice {
384
                vtable,
13✔
385
                building_item,
13✔
386
            } = &frames[0].tracker
14✔
387
            {
388
                if *building_item {
13✔
389
                    return Err(ReflectError::OperationFailed {
×
390
                        shape: frames[0].shape,
×
391
                        operation: "still building an item, finish it first",
×
392
                    });
×
393
                }
13✔
394

395
                // Convert the builder to Arc<[T]>
396
                let vtable = *vtable;
13✔
397
                let builder_ptr = unsafe { frames[0].data.assume_init() };
13✔
398
                let arc_ptr = unsafe { (vtable.convert_fn)(builder_ptr) };
13✔
399

400
                // Update the frame to store the Arc
401
                frames[0].data = PtrUninit::new(unsafe {
13✔
402
                    NonNull::new_unchecked(arc_ptr.as_byte_ptr() as *mut u8)
13✔
403
                });
13✔
404
                frames[0].tracker = Tracker::Scalar;
13✔
405
                frames[0].is_init = true;
13✔
406
                // The builder memory has been consumed by convert_fn, so we no longer own it
407
                frames[0].ownership = FrameOwnership::ManagedElsewhere;
13✔
408

409
                return Ok(self);
13✔
410
            }
1✔
411
        }
19,991✔
412

413
        if self.frames().len() <= 1 {
19,992✔
414
            // Never pop the last/root frame - this indicates a broken state machine
415
            // No need to poison - returning Err consumes self, Drop will handle cleanup
416
            return Err(ReflectError::InvariantViolation {
1✔
417
                invariant: "Partial::end() called with only one frame on the stack",
1✔
418
            });
1✔
419
        }
19,991✔
420

421
        // In deferred mode, cannot pop below the start depth
422
        if let Some(start_depth) = self.start_depth() {
19,991✔
423
            if self.frames().len() <= start_depth {
1,236✔
424
                // No need to poison - returning Err consumes self, Drop will handle cleanup
425
                return Err(ReflectError::InvariantViolation {
×
426
                    invariant: "Partial::end() called but would pop below deferred start depth",
×
427
                });
×
428
            }
1,236✔
429
        }
18,755✔
430

431
        // Require that the top frame is fully initialized before popping.
432
        // Skip this check in deferred mode - validation happens in finish_deferred().
433
        // EXCEPT for collection items (map, list, set, option) which must be fully
434
        // initialized before insertion/completion.
435
        let requires_full_init = if !self.is_deferred() {
19,991✔
436
            true
18,755✔
437
        } else {
438
            // In deferred mode, first check if this frame will be stored (tracked field).
439
            // If so, skip full init check - the frame will be stored for later completion.
440
            let is_tracked_frame = if let FrameMode::Deferred {
1,236✔
441
                stack,
1,236✔
442
                start_depth,
1,236✔
443
                current_path,
1,236✔
444
                ..
445
            } = &self.mode
1,236✔
446
            {
447
                // Path depth should match the relative frame depth for a tracked field.
448
                // frames.len() - start_depth should equal path.len() for tracked fields.
449
                let relative_depth = stack.len() - *start_depth;
1,236✔
450
                !current_path.is_empty() && current_path.len() == relative_depth
1,236✔
451
            } else {
452
                false
×
453
            };
454

455
            if is_tracked_frame {
1,236✔
456
                // This frame will be stored in deferred mode - don't require full init
457
                false
933✔
458
            } else {
459
                // Check if parent is a collection that requires fully initialized items
460
                if self.frames().len() >= 2 {
303✔
461
                    let frame_len = self.frames().len();
303✔
462
                    let parent_frame = &self.frames()[frame_len - 2];
303✔
463
                    matches!(
108✔
464
                        parent_frame.tracker,
35✔
465
                        Tracker::Map { .. }
466
                            | Tracker::List { .. }
467
                            | Tracker::Set { .. }
468
                            | Tracker::Option { .. }
469
                            | Tracker::Result { .. }
470
                            | Tracker::DynamicValue {
471
                                state: DynamicValueState::Array { .. }
472
                            }
473
                    )
474
                } else {
NEW
475
                    false
×
476
                }
477
            }
478
        };
479

480
        if requires_full_init {
19,991✔
481
            let frame = self.frames().last().unwrap();
18,950✔
482
            crate::trace!(
483
                "end(): Checking full init for {}, tracker={:?}, is_init={}",
484
                frame.shape,
485
                frame.tracker.kind(),
486
                frame.is_init
487
            );
488
            let result = frame.require_full_initialization();
18,950✔
489
            crate::trace!(
490
                "end(): require_full_initialization result: {:?}",
491
                result.is_ok()
492
            );
493
            result?
18,950✔
494
        }
1,041✔
495

496
        // Pop the frame and save its data pointer for SmartPointer handling
497
        let mut popped_frame = self.frames_mut().pop().unwrap();
19,986✔
498

499
        // In deferred mode, store the frame for potential re-entry and skip
500
        // the normal parent-updating logic. The frame will be finalized later
501
        // in finish_deferred().
502
        //
503
        // We only store if the path depth matches the frame depth, meaning we're
504
        // ending a tracked struct/enum field, not something like begin_some()
505
        // or a field inside a collection item.
506
        if let FrameMode::Deferred {
507
            stack,
1,236✔
508
            start_depth,
1,236✔
509
            current_path,
1,236✔
510
            stored_frames,
1,236✔
511
            ..
512
        } = &mut self.mode
19,986✔
513
        {
514
            // Path depth should match the relative frame depth for a tracked field.
515
            // After popping: frames.len() - start_depth + 1 should equal path.len()
516
            // for fields entered via begin_field (not begin_some/begin_inner).
517
            let relative_depth = stack.len() - *start_depth + 1;
1,236✔
518
            let is_tracked_field = !current_path.is_empty() && current_path.len() == relative_depth;
1,236✔
519

520
            if is_tracked_field {
1,236✔
521
                trace!(
522
                    "end(): Storing frame for deferred path {:?}, shape {}",
523
                    current_path, popped_frame.shape
524
                );
525

526
                // Store the frame at the current path
527
                let path = current_path.clone();
933✔
528
                stored_frames.insert(path, popped_frame);
933✔
529

530
                // Pop from current_path
531
                current_path.pop();
933✔
532

533
                // Clear parent's current_child tracking
534
                if let Some(parent_frame) = stack.last_mut() {
933✔
535
                    parent_frame.tracker.clear_current_child();
933✔
536
                }
933✔
537

538
                return Ok(self);
933✔
539
            }
303✔
540
        }
18,750✔
541

542
        // check if this needs deserialization from a different shape
543
        if popped_frame.using_custom_deserialization {
19,053✔
544
            // First try field-level proxy
545
            let deserialize_with = self
62✔
546
                .parent_field()
62✔
547
                .and_then(|field| field.proxy_convert_in_fn());
62✔
548

549
            // Fall back to shape-level proxy stored in the frame
550
            #[cfg(feature = "alloc")]
551
            let deserialize_with =
62✔
552
                deserialize_with.or_else(|| popped_frame.shape_level_proxy.map(|p| p.convert_in));
62✔
553

554
            if let Some(deserialize_with) = deserialize_with {
62✔
555
                let parent_frame = self.frames_mut().last_mut().unwrap();
62✔
556

557
                trace!(
558
                    "Detected custom conversion needed from {} to {}",
559
                    popped_frame.shape, parent_frame.shape
560
                );
561

562
                unsafe {
563
                    let res = {
62✔
564
                        let inner_value_ptr = popped_frame.data.assume_init().as_const();
62✔
565
                        (deserialize_with)(inner_value_ptr, parent_frame.data)
62✔
566
                    };
567
                    let popped_frame_shape = popped_frame.shape;
62✔
568

569
                    // Note: We do NOT call deinit() here because deserialize_with uses
570
                    // ptr::read to take ownership of the source value. Calling deinit()
571
                    // would cause a double-free. We mark is_init as false to satisfy
572
                    // dealloc()'s assertion, then deallocate the memory.
573
                    popped_frame.is_init = false;
62✔
574
                    popped_frame.dealloc();
62✔
575
                    let rptr = res.map_err(|message| ReflectError::CustomDeserializationError {
62✔
576
                        message,
6✔
577
                        src_shape: popped_frame_shape,
6✔
578
                        dst_shape: parent_frame.shape,
6✔
579
                    })?;
6✔
580
                    if rptr.as_uninit() != parent_frame.data {
56✔
581
                        return Err(ReflectError::CustomDeserializationError {
×
582
                            message: "deserialize_with did not return the expected pointer".into(),
×
583
                            src_shape: popped_frame_shape,
×
584
                            dst_shape: parent_frame.shape,
×
585
                        });
×
586
                    }
56✔
587
                    parent_frame.mark_as_init();
56✔
588
                }
589
                return Ok(self);
56✔
590
            }
×
591
        }
18,991✔
592

593
        // Update parent frame's tracking when popping from a child
594
        let parent_frame = self.frames_mut().last_mut().unwrap();
18,991✔
595

596
        crate::trace!(
597
            "end(): Popped {} (tracker {:?}), Parent {} (tracker {:?})",
598
            popped_frame.shape,
599
            popped_frame.tracker.kind(),
600
            parent_frame.shape,
601
            parent_frame.tracker.kind()
602
        );
603

604
        // Check if we need to do a conversion - this happens when:
605
        // 1. The parent frame has an inner type that matches the popped frame's shape
606
        // 2. The parent frame has try_from
607
        // 3. The parent frame is not yet initialized
608
        // 4. The parent frame's tracker is Scalar (not Option, SmartPointer, etc.)
609
        //    This ensures we only do conversion when begin_inner was used, not begin_some
610
        let needs_conversion = !parent_frame.is_init
18,991✔
611
            && matches!(parent_frame.tracker, Tracker::Scalar)
3,840✔
612
            && parent_frame.shape.inner.is_some()
109✔
613
            && parent_frame.shape.inner.unwrap() == popped_frame.shape
109✔
614
            && parent_frame.shape.vtable.try_from.is_some();
101✔
615

616
        if needs_conversion {
18,991✔
617
            trace!(
618
                "Detected implicit conversion needed from {} to {}",
619
                popped_frame.shape, parent_frame.shape
620
            );
621

622
            // The conversion requires the source frame to be fully initialized
623
            // (we're about to call assume_init() and pass to try_from)
624
            if let Err(e) = popped_frame.require_full_initialization() {
101✔
625
                // Deallocate the memory since the frame wasn't fully initialized
626
                if let FrameOwnership::Owned = popped_frame.ownership {
×
627
                    if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
×
628
                        if layout.size() > 0 {
×
629
                            trace!(
630
                                "Deallocating uninitialized conversion frame memory: size={}, align={}",
631
                                layout.size(),
632
                                layout.align()
633
                            );
634
                            unsafe {
×
635
                                ::alloc::alloc::dealloc(
×
636
                                    popped_frame.data.as_mut_byte_ptr(),
×
637
                                    layout,
×
638
                                );
×
639
                            }
×
640
                        }
×
641
                    }
×
642
                }
×
643
                return Err(e);
×
644
            }
101✔
645

646
            // Perform the conversion
647
            if let Some(try_from_fn) = parent_frame.shape.vtable.try_from {
101✔
648
                let inner_ptr = unsafe { popped_frame.data.assume_init().as_const() };
101✔
649
                let inner_shape = popped_frame.shape;
101✔
650

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

654
                if let Err(e) = result {
101✔
655
                    trace!("Conversion failed: {e:?}");
656

657
                    // Deallocate the inner value's memory since conversion failed
658
                    if let FrameOwnership::Owned = popped_frame.ownership {
2✔
659
                        if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
2✔
660
                            if layout.size() > 0 {
2✔
661
                                trace!(
662
                                    "Deallocating conversion frame memory after failure: size={}, align={}",
663
                                    layout.size(),
664
                                    layout.align()
665
                                );
666
                                unsafe {
2✔
667
                                    ::alloc::alloc::dealloc(
2✔
668
                                        popped_frame.data.as_mut_byte_ptr(),
2✔
669
                                        layout,
2✔
670
                                    );
2✔
671
                                }
2✔
672
                            }
×
673
                        }
×
674
                    }
×
675

676
                    return Err(ReflectError::TryFromError {
2✔
677
                        src_shape: inner_shape,
2✔
678
                        dst_shape: parent_frame.shape,
2✔
679
                        inner: e,
2✔
680
                    });
2✔
681
                }
99✔
682

683
                trace!("Conversion succeeded, marking parent as initialized");
684
                parent_frame.is_init = true;
99✔
685

686
                // Deallocate the inner value's memory since try_from consumed it
687
                if let FrameOwnership::Owned = popped_frame.ownership {
99✔
688
                    if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
99✔
689
                        if layout.size() > 0 {
99✔
690
                            trace!(
691
                                "Deallocating conversion frame memory: size={}, align={}",
692
                                layout.size(),
693
                                layout.align()
694
                            );
695
                            unsafe {
99✔
696
                                ::alloc::alloc::dealloc(
99✔
697
                                    popped_frame.data.as_mut_byte_ptr(),
99✔
698
                                    layout,
99✔
699
                                );
99✔
700
                            }
99✔
701
                        }
×
702
                    }
×
703
                }
×
704

705
                return Ok(self);
99✔
706
            }
×
707
        }
18,890✔
708

709
        // For Field-owned frames, reclaim responsibility in parent's tracker
710
        // Only mark as initialized if the child frame was actually initialized.
711
        // This prevents double-free when begin_inner/begin_some drops a value via
712
        // prepare_for_reinitialization but then fails, leaving the child uninitialized.
713
        //
714
        // We use require_full_initialization() rather than just is_init because:
715
        // - Scalar frames use is_init as the source of truth
716
        // - Struct/Array/Enum frames use their iset/data as the source of truth
717
        //   (is_init may never be set to true for these tracker types)
718
        if let FrameOwnership::Field { field_idx } = popped_frame.ownership {
18,890✔
719
            let child_is_initialized = popped_frame.require_full_initialization().is_ok();
3,395✔
720
            match &mut parent_frame.tracker {
3,395✔
721
                Tracker::Struct {
722
                    iset,
2,955✔
723
                    current_child,
2,955✔
724
                } => {
725
                    if child_is_initialized {
2,955✔
726
                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
2,955✔
727
                    }
2,955✔
728
                    *current_child = None;
2,955✔
729
                }
730
                Tracker::Array {
731
                    iset,
145✔
732
                    current_child,
145✔
733
                } => {
734
                    if child_is_initialized {
145✔
735
                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
145✔
736
                    }
145✔
737
                    *current_child = None;
145✔
738
                }
739
                Tracker::Enum {
740
                    data,
295✔
741
                    current_child,
295✔
742
                    ..
743
                } => {
744
                    if child_is_initialized {
295✔
745
                        data.set(field_idx); // Parent reclaims responsibility only if child was init
295✔
746
                    }
295✔
747
                    *current_child = None;
295✔
748
                }
749
                _ => {}
×
750
            }
751
            return Ok(self);
3,395✔
752
        }
15,495✔
753

754
        match &mut parent_frame.tracker {
12,837✔
755
            Tracker::SmartPointer => {
756
                // We just popped the inner value frame, so now we need to create the smart pointer
757
                if let Def::Pointer(smart_ptr_def) = parent_frame.shape.def {
63✔
758
                    // The inner value must be fully initialized before we can create the smart pointer
759
                    if let Err(e) = popped_frame.require_full_initialization() {
63✔
760
                        // Inner value wasn't initialized, deallocate and return error
761
                        popped_frame.deinit();
×
762
                        popped_frame.dealloc();
×
763
                        return Err(e);
×
764
                    }
63✔
765

766
                    let Some(new_into_fn) = smart_ptr_def.vtable.new_into_fn else {
63✔
767
                        popped_frame.deinit();
×
768
                        popped_frame.dealloc();
×
769
                        return Err(ReflectError::OperationFailed {
×
770
                            shape: parent_frame.shape,
×
771
                            operation: "SmartPointer missing new_into_fn",
×
772
                        });
×
773
                    };
774

775
                    // The child frame contained the inner value
776
                    let inner_ptr = PtrMut::new(unsafe {
63✔
777
                        NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
63✔
778
                    });
779

780
                    // Use new_into_fn to create the Box
781
                    unsafe {
63✔
782
                        new_into_fn(parent_frame.data, inner_ptr);
63✔
783
                    }
63✔
784

785
                    // We just moved out of it
786
                    popped_frame.tracker = Tracker::Scalar;
63✔
787
                    popped_frame.is_init = false;
63✔
788

789
                    // Deallocate the inner value's memory since new_into_fn moved it
790
                    popped_frame.dealloc();
63✔
791

792
                    parent_frame.is_init = true;
63✔
793
                }
×
794
            }
795
            Tracker::List { current_child } if parent_frame.is_init => {
12,837✔
796
                if *current_child {
12,837✔
797
                    // We just popped an element frame, now push it to the list
798
                    if let Def::List(list_def) = parent_frame.shape.def {
12,837✔
799
                        let Some(push_fn) = list_def.vtable.push else {
12,837✔
800
                            return Err(ReflectError::OperationFailed {
×
801
                                shape: parent_frame.shape,
×
802
                                operation: "List missing push function",
×
803
                            });
×
804
                        };
805

806
                        // The child frame contained the element value
807
                        let element_ptr = PtrMut::new(unsafe {
12,837✔
808
                            NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
12,837✔
809
                        });
810

811
                        // Use push to add element to the list
812
                        unsafe {
12,837✔
813
                            push_fn(
12,837✔
814
                                PtrMut::new(NonNull::new_unchecked(
12,837✔
815
                                    parent_frame.data.as_mut_byte_ptr(),
12,837✔
816
                                )),
12,837✔
817
                                element_ptr,
12,837✔
818
                            );
12,837✔
819
                        }
12,837✔
820

821
                        // Push moved out of popped_frame
822
                        popped_frame.tracker = Tracker::Scalar;
12,837✔
823
                        popped_frame.is_init = false;
12,837✔
824
                        popped_frame.dealloc();
12,837✔
825

826
                        *current_child = false;
12,837✔
827
                    }
×
828
                }
×
829
            }
830
            Tracker::Map { insert_state } if parent_frame.is_init => {
2,224✔
831
                match insert_state {
2,224✔
832
                    MapInsertState::PushingKey { key_ptr, .. } => {
1,115✔
833
                        // We just popped the key frame - mark key as initialized and transition
1,115✔
834
                        // to PushingValue state
1,115✔
835
                        *insert_state = MapInsertState::PushingValue {
1,115✔
836
                            key_ptr: *key_ptr,
1,115✔
837
                            value_ptr: None,
1,115✔
838
                            value_initialized: false,
1,115✔
839
                        };
1,115✔
840
                    }
1,115✔
841
                    MapInsertState::PushingValue {
842
                        key_ptr, value_ptr, ..
1,109✔
843
                    } => {
844
                        // We just popped the value frame, now insert the pair
845
                        if let (Some(value_ptr), Def::Map(map_def)) =
1,109✔
846
                            (value_ptr, parent_frame.shape.def)
1,109✔
847
                        {
848
                            let insert_fn = map_def.vtable.insert_fn;
1,109✔
849

850
                            // Use insert to add key-value pair to the map
851
                            unsafe {
1,109✔
852
                                insert_fn(
1,109✔
853
                                    PtrMut::new(NonNull::new_unchecked(
1,109✔
854
                                        parent_frame.data.as_mut_byte_ptr(),
1,109✔
855
                                    )),
1,109✔
856
                                    PtrMut::new(NonNull::new_unchecked(key_ptr.as_mut_byte_ptr())),
1,109✔
857
                                    PtrMut::new(NonNull::new_unchecked(
1,109✔
858
                                        value_ptr.as_mut_byte_ptr(),
1,109✔
859
                                    )),
1,109✔
860
                                );
1,109✔
861
                            }
1,109✔
862

863
                            // Note: We don't deallocate the key and value memory here.
864
                            // The insert function has semantically moved the values into the map,
865
                            // but we still need to deallocate the temporary buffers.
866
                            // However, since we don't have frames for them anymore (they were popped),
867
                            // we need to handle deallocation here.
868
                            if let Ok(key_shape) = map_def.k().layout.sized_layout() {
1,109✔
869
                                if key_shape.size() > 0 {
1,109✔
870
                                    unsafe {
1,109✔
871
                                        ::alloc::alloc::dealloc(
1,109✔
872
                                            key_ptr.as_mut_byte_ptr(),
1,109✔
873
                                            key_shape,
1,109✔
874
                                        );
1,109✔
875
                                    }
1,109✔
876
                                }
×
877
                            }
×
878
                            if let Ok(value_shape) = map_def.v().layout.sized_layout() {
1,109✔
879
                                if value_shape.size() > 0 {
1,109✔
880
                                    unsafe {
1,109✔
881
                                        ::alloc::alloc::dealloc(
1,109✔
882
                                            value_ptr.as_mut_byte_ptr(),
1,109✔
883
                                            value_shape,
1,109✔
884
                                        );
1,109✔
885
                                    }
1,109✔
886
                                }
×
887
                            }
×
888

889
                            // Reset to idle state
890
                            *insert_state = MapInsertState::Idle;
1,109✔
891
                        }
×
892
                    }
893
                    MapInsertState::Idle => {
×
894
                        // Nothing to do
×
895
                    }
×
896
                }
897
            }
898
            Tracker::Set { current_child } if parent_frame.is_init => {
42✔
899
                if *current_child {
42✔
900
                    // We just popped an element frame, now insert it into the set
901
                    if let Def::Set(set_def) = parent_frame.shape.def {
42✔
902
                        let insert_fn = set_def.vtable.insert_fn;
42✔
903

42✔
904
                        // The child frame contained the element value
42✔
905
                        let element_ptr = PtrMut::new(unsafe {
42✔
906
                            NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
42✔
907
                        });
42✔
908

42✔
909
                        // Use insert to add element to the set
42✔
910
                        unsafe {
42✔
911
                            insert_fn(
42✔
912
                                PtrMut::new(NonNull::new_unchecked(
42✔
913
                                    parent_frame.data.as_mut_byte_ptr(),
42✔
914
                                )),
42✔
915
                                element_ptr,
42✔
916
                            );
42✔
917
                        }
42✔
918

42✔
919
                        // Insert moved out of popped_frame
42✔
920
                        popped_frame.tracker = Tracker::Scalar;
42✔
921
                        popped_frame.is_init = false;
42✔
922
                        popped_frame.dealloc();
42✔
923

42✔
924
                        *current_child = false;
42✔
925
                    }
42✔
926
                }
×
927
            }
928
            Tracker::Option { building_inner } => {
234✔
929
                crate::trace!(
930
                    "end(): matched Tracker::Option, building_inner={}",
931
                    *building_inner
932
                );
933
                // We just popped the inner value frame for an Option's Some variant
934
                if *building_inner {
234✔
935
                    if let Def::Option(option_def) = parent_frame.shape.def {
234✔
936
                        // Use the Option vtable to initialize Some(inner_value)
937
                        let init_some_fn = option_def.vtable.init_some_fn;
234✔
938

939
                        // The popped frame contains the inner value
940
                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
234✔
941

942
                        // Initialize the Option as Some(inner_value)
943
                        unsafe {
234✔
944
                            init_some_fn(parent_frame.data, inner_value_ptr);
234✔
945
                        }
234✔
946

947
                        // Deallocate the inner value's memory since init_some_fn moved it
948
                        if let FrameOwnership::Owned = popped_frame.ownership {
234✔
949
                            if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
234✔
950
                                if layout.size() > 0 {
234✔
951
                                    unsafe {
234✔
952
                                        ::alloc::alloc::dealloc(
234✔
953
                                            popped_frame.data.as_mut_byte_ptr(),
234✔
954
                                            layout,
234✔
955
                                        );
234✔
956
                                    }
234✔
957
                                }
×
958
                            }
×
959
                        }
×
960

961
                        // Mark that we're no longer building the inner value
962
                        *building_inner = false;
234✔
963
                        crate::trace!("end(): set building_inner to false");
964
                        // Mark the Option as initialized
965
                        parent_frame.is_init = true;
234✔
966
                        crate::trace!("end(): set parent_frame.is_init to true");
967
                    } else {
968
                        return Err(ReflectError::OperationFailed {
×
969
                            shape: parent_frame.shape,
×
970
                            operation: "Option frame without Option definition",
×
971
                        });
×
972
                    }
973
                } else {
974
                    // building_inner is false - the Option was already initialized but
975
                    // begin_some was called again. The popped frame was not used to
976
                    // initialize the Option, so we need to clean it up.
977
                    popped_frame.deinit();
×
978
                    if let FrameOwnership::Owned = popped_frame.ownership {
×
979
                        if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
×
980
                            if layout.size() > 0 {
×
981
                                unsafe {
×
982
                                    ::alloc::alloc::dealloc(
×
983
                                        popped_frame.data.as_mut_byte_ptr(),
×
984
                                        layout,
×
985
                                    );
×
986
                                }
×
987
                            }
×
988
                        }
×
989
                    }
×
990
                }
991
            }
992
            Tracker::Result {
993
                is_ok,
6✔
994
                building_inner,
6✔
995
            } => {
996
                crate::trace!(
997
                    "end(): matched Tracker::Result, is_ok={}, building_inner={}",
998
                    *is_ok,
999
                    *building_inner
1000
                );
1001
                // We just popped the inner value frame for a Result's Ok or Err variant
1002
                if *building_inner {
6✔
1003
                    if let Def::Result(result_def) = parent_frame.shape.def {
6✔
1004
                        // The popped frame contains the inner value
1005
                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
6✔
1006

1007
                        // Initialize the Result as Ok(inner_value) or Err(inner_value)
1008
                        if *is_ok {
6✔
1009
                            let init_ok_fn = result_def.vtable.init_ok_fn;
3✔
1010
                            unsafe {
3✔
1011
                                init_ok_fn(parent_frame.data, inner_value_ptr);
3✔
1012
                            }
3✔
1013
                        } else {
1014
                            let init_err_fn = result_def.vtable.init_err_fn;
3✔
1015
                            unsafe {
3✔
1016
                                init_err_fn(parent_frame.data, inner_value_ptr);
3✔
1017
                            }
3✔
1018
                        }
1019

1020
                        // Deallocate the inner value's memory since init_ok/err_fn moved it
1021
                        if let FrameOwnership::Owned = popped_frame.ownership {
6✔
1022
                            if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
6✔
1023
                                if layout.size() > 0 {
6✔
1024
                                    unsafe {
6✔
1025
                                        ::alloc::alloc::dealloc(
6✔
1026
                                            popped_frame.data.as_mut_byte_ptr(),
6✔
1027
                                            layout,
6✔
1028
                                        );
6✔
1029
                                    }
6✔
1030
                                }
×
1031
                            }
×
1032
                        }
×
1033

1034
                        // Mark that we're no longer building the inner value
1035
                        *building_inner = false;
6✔
1036
                        crate::trace!("end(): set building_inner to false");
1037
                        // Mark the Result as initialized
1038
                        parent_frame.is_init = true;
6✔
1039
                        crate::trace!("end(): set parent_frame.is_init to true");
1040
                    } else {
1041
                        return Err(ReflectError::OperationFailed {
×
1042
                            shape: parent_frame.shape,
×
1043
                            operation: "Result frame without Result definition",
×
1044
                        });
×
1045
                    }
1046
                } else {
1047
                    // building_inner is false - the Result was already initialized but
1048
                    // begin_ok/begin_err was called again. The popped frame was not used to
1049
                    // initialize the Result, so we need to clean it up.
1050
                    popped_frame.deinit();
×
1051
                    if let FrameOwnership::Owned = popped_frame.ownership {
×
1052
                        if let Ok(layout) = popped_frame.shape.layout.sized_layout() {
×
1053
                            if layout.size() > 0 {
×
1054
                                unsafe {
×
1055
                                    ::alloc::alloc::dealloc(
×
1056
                                        popped_frame.data.as_mut_byte_ptr(),
×
1057
                                        layout,
×
1058
                                    );
×
1059
                                }
×
1060
                            }
×
1061
                        }
×
1062
                    }
×
1063
                }
1064
            }
1065
            Tracker::Scalar => {
1066
                // the main case here is: the popped frame was a `String` and the
1067
                // parent frame is an `Arc<str>`, `Box<str>` etc.
1068
                match &parent_frame.shape.def {
8✔
1069
                    Def::Pointer(smart_ptr_def) => {
8✔
1070
                        let pointee =
8✔
1071
                            smart_ptr_def
8✔
1072
                                .pointee()
8✔
1073
                                .ok_or(ReflectError::InvariantViolation {
8✔
1074
                                    invariant: "pointer type doesn't have a pointee",
8✔
1075
                                })?;
8✔
1076

1077
                        if !pointee.is_shape(str::SHAPE) {
8✔
1078
                            return Err(ReflectError::InvariantViolation {
×
1079
                                invariant: "only T=str is supported when building SmartPointer<T> and T is unsized",
×
1080
                            });
×
1081
                        }
8✔
1082

1083
                        if !popped_frame.shape.is_shape(String::SHAPE) {
8✔
1084
                            return Err(ReflectError::InvariantViolation {
×
1085
                                invariant: "the popped frame should be String when building a SmartPointer<T>",
×
1086
                            });
×
1087
                        }
8✔
1088

1089
                        popped_frame.require_full_initialization()?;
8✔
1090

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

1098
                        let Some(known) = smart_ptr_def.known else {
8✔
1099
                            return Err(ReflectError::OperationFailed {
×
1100
                                shape: parent_shape,
×
1101
                                operation: "SmartPointerStr for unknown smart pointer kind",
×
1102
                            });
×
1103
                        };
1104

1105
                        parent_frame.deinit();
8✔
1106

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

1111
                        match known {
8✔
1112
                            KnownPointer::Box => {
1113
                                let boxed: Box<str> = string_value.into_boxed_str();
2✔
1114
                                unsafe {
2✔
1115
                                    core::ptr::write(
2✔
1116
                                        parent_frame.data.as_mut_byte_ptr() as *mut Box<str>,
2✔
1117
                                        boxed,
2✔
1118
                                    );
2✔
1119
                                }
2✔
1120
                            }
1121
                            KnownPointer::Arc => {
1122
                                let arc: Arc<str> = Arc::from(string_value.into_boxed_str());
2✔
1123
                                unsafe {
2✔
1124
                                    core::ptr::write(
2✔
1125
                                        parent_frame.data.as_mut_byte_ptr() as *mut Arc<str>,
2✔
1126
                                        arc,
2✔
1127
                                    );
2✔
1128
                                }
2✔
1129
                            }
1130
                            KnownPointer::Rc => {
1131
                                let rc: Rc<str> = Rc::from(string_value.into_boxed_str());
4✔
1132
                                unsafe {
4✔
1133
                                    core::ptr::write(
4✔
1134
                                        parent_frame.data.as_mut_byte_ptr() as *mut Rc<str>,
4✔
1135
                                        rc,
4✔
1136
                                    );
4✔
1137
                                }
4✔
1138
                            }
1139
                            _ => {
1140
                                return Err(ReflectError::OperationFailed {
×
1141
                                    shape: parent_shape,
×
1142
                                    operation: "Don't know how to build this pointer type",
×
1143
                                });
×
1144
                            }
1145
                        }
1146

1147
                        parent_frame.is_init = true;
8✔
1148

1149
                        popped_frame.tracker = Tracker::Scalar;
8✔
1150
                        popped_frame.is_init = false;
8✔
1151
                        popped_frame.dealloc();
8✔
1152
                    }
1153
                    _ => {
1154
                        // This can happen if begin_inner() was called on a type that
1155
                        // has shape.inner but isn't a SmartPointer (e.g., Option).
1156
                        // In this case, we can't complete the conversion, so return error.
1157
                        return Err(ReflectError::OperationFailed {
×
1158
                            shape: parent_frame.shape,
×
1159
                            operation: "end() called but parent has Uninit/Init tracker and isn't a SmartPointer",
×
1160
                        });
×
1161
                    }
1162
                }
1163
            }
1164
            Tracker::SmartPointerSlice {
1165
                vtable,
33✔
1166
                building_item,
33✔
1167
            } => {
1168
                if *building_item {
33✔
1169
                    // We just popped an element frame, now push it to the slice builder
1170
                    let element_ptr = PtrMut::new(unsafe {
33✔
1171
                        NonNull::new_unchecked(popped_frame.data.as_mut_byte_ptr())
33✔
1172
                    });
1173

1174
                    // Use the slice builder's push_fn to add the element
1175
                    crate::trace!("Pushing element to slice builder");
1176
                    unsafe {
33✔
1177
                        let parent_ptr = parent_frame.data.assume_init();
33✔
1178
                        (vtable.push_fn)(parent_ptr, element_ptr);
33✔
1179
                    }
33✔
1180

1181
                    popped_frame.tracker = Tracker::Scalar;
33✔
1182
                    popped_frame.is_init = false;
33✔
1183
                    popped_frame.dealloc();
33✔
1184

1185
                    if let Tracker::SmartPointerSlice {
1186
                        building_item: bi, ..
33✔
1187
                    } = &mut parent_frame.tracker
33✔
1188
                    {
33✔
1189
                        *bi = false;
33✔
1190
                    }
33✔
1191
                }
×
1192
            }
1193
            Tracker::DynamicValue {
1194
                state: DynamicValueState::Array { building_element },
22✔
1195
            } => {
1196
                if *building_element {
22✔
1197
                    // Check that the element is initialized before pushing
1198
                    if !popped_frame.is_init {
22✔
1199
                        // Element was never set - clean up and return error
1200
                        let shape = parent_frame.shape;
×
1201
                        popped_frame.dealloc();
×
1202
                        *building_element = false;
×
1203
                        // No need to poison - returning Err consumes self, Drop will handle cleanup
1204
                        return Err(ReflectError::OperationFailed {
×
1205
                            shape,
×
1206
                            operation: "end() called but array element was never initialized",
×
1207
                        });
×
1208
                    }
22✔
1209

1210
                    // We just popped an element frame, now push it to the dynamic array
1211
                    if let Def::DynamicValue(dyn_def) = parent_frame.shape.def {
22✔
1212
                        // Get mutable pointers - both array and element need PtrMut
22✔
1213
                        let array_ptr = unsafe { parent_frame.data.assume_init() };
22✔
1214
                        let element_ptr = unsafe { popped_frame.data.assume_init() };
22✔
1215

22✔
1216
                        // Use push_array_element to add element to the array
22✔
1217
                        unsafe {
22✔
1218
                            (dyn_def.vtable.push_array_element)(array_ptr, element_ptr);
22✔
1219
                        }
22✔
1220

22✔
1221
                        // Push moved out of popped_frame
22✔
1222
                        popped_frame.tracker = Tracker::Scalar;
22✔
1223
                        popped_frame.is_init = false;
22✔
1224
                        popped_frame.dealloc();
22✔
1225

22✔
1226
                        *building_element = false;
22✔
1227
                    }
22✔
1228
                }
×
1229
            }
1230
            Tracker::DynamicValue {
1231
                state: DynamicValueState::Object { insert_state },
26✔
1232
            } => {
1233
                if let DynamicObjectInsertState::BuildingValue { key } = insert_state {
26✔
1234
                    // Check that the value is initialized before inserting
1235
                    if !popped_frame.is_init {
26✔
1236
                        // Value was never set - clean up and return error
1237
                        let shape = parent_frame.shape;
×
1238
                        popped_frame.dealloc();
×
1239
                        *insert_state = DynamicObjectInsertState::Idle;
×
1240
                        // No need to poison - returning Err consumes self, Drop will handle cleanup
1241
                        return Err(ReflectError::OperationFailed {
×
1242
                            shape,
×
1243
                            operation: "end() called but object entry value was never initialized",
×
1244
                        });
×
1245
                    }
26✔
1246

1247
                    // We just popped a value frame, now insert it into the dynamic object
1248
                    if let Def::DynamicValue(dyn_def) = parent_frame.shape.def {
26✔
1249
                        // Get mutable pointers - both object and value need PtrMut
26✔
1250
                        let object_ptr = unsafe { parent_frame.data.assume_init() };
26✔
1251
                        let value_ptr = unsafe { popped_frame.data.assume_init() };
26✔
1252

26✔
1253
                        // Use insert_object_entry to add the key-value pair
26✔
1254
                        unsafe {
26✔
1255
                            (dyn_def.vtable.insert_object_entry)(object_ptr, key, value_ptr);
26✔
1256
                        }
26✔
1257

26✔
1258
                        // Insert moved out of popped_frame
26✔
1259
                        popped_frame.tracker = Tracker::Scalar;
26✔
1260
                        popped_frame.is_init = false;
26✔
1261
                        popped_frame.dealloc();
26✔
1262

26✔
1263
                        // Reset insert state to Idle
26✔
1264
                        *insert_state = DynamicObjectInsertState::Idle;
26✔
1265
                    }
26✔
1266
                }
×
1267
            }
1268
            _ => {}
×
1269
        }
1270

1271
        Ok(self)
15,495✔
1272
    }
20,005✔
1273

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

1279
        let mut path_components = Vec::new();
3,165✔
1280
        // The stack of enum/struct/sequence names currently in context.
1281
        // Start from root and build upwards.
1282
        for (i, frame) in self.frames().iter().enumerate() {
6,616✔
1283
            match frame.shape.ty {
6,616✔
1284
                Type::User(user_type) => match user_type {
5,688✔
1285
                    UserType::Struct(struct_type) => {
3,379✔
1286
                        // Try to get currently active field index
1287
                        let mut field_str = None;
3,379✔
1288
                        if let Tracker::Struct {
1289
                            current_child: Some(idx),
2,212✔
1290
                            ..
1291
                        } = &frame.tracker
2,605✔
1292
                        {
1293
                            if let Some(field) = struct_type.fields.get(*idx) {
2,212✔
1294
                                field_str = Some(field.name);
2,212✔
1295
                            }
2,212✔
1296
                        }
1,167✔
1297
                        if i == 0 {
3,379✔
1298
                            // Use Display for the root struct shape
2,773✔
1299
                            path_components.push(format!("{}", frame.shape));
2,773✔
1300
                        }
2,773✔
1301
                        if let Some(field_name) = field_str {
3,379✔
1302
                            path_components.push(format!(".{field_name}"));
2,212✔
1303
                        }
2,212✔
1304
                    }
1305
                    UserType::Enum(_enum_type) => {
500✔
1306
                        // Try to get currently active variant and field
1307
                        if let Tracker::Enum {
1308
                            variant,
295✔
1309
                            current_child,
295✔
1310
                            ..
1311
                        } = &frame.tracker
500✔
1312
                        {
1313
                            if i == 0 {
295✔
1314
                                // Use Display for the root enum shape
100✔
1315
                                path_components.push(format!("{}", frame.shape));
100✔
1316
                            }
195✔
1317
                            path_components.push(format!("::{}", variant.name));
295✔
1318
                            if let Some(idx) = *current_child {
295✔
1319
                                if let Some(field) = variant.data.fields.get(idx) {
187✔
1320
                                    path_components.push(format!(".{}", field.name));
187✔
1321
                                }
187✔
1322
                            }
108✔
1323
                        } else if i == 0 {
205✔
1324
                            // just the enum display
44✔
1325
                            path_components.push(format!("{}", frame.shape));
44✔
1326
                        }
161✔
1327
                    }
1328
                    UserType::Union(_union_type) => {
×
1329
                        path_components.push(format!("{}", frame.shape));
×
1330
                    }
×
1331
                    UserType::Opaque => {
1,809✔
1332
                        path_components.push("<opaque>".to_string());
1,809✔
1333
                    }
1,809✔
1334
                },
1335
                Type::Sequence(seq_type) => match seq_type {
29✔
1336
                    facet_core::SequenceType::Array(_array_def) => {
29✔
1337
                        // Try to show current element index
1338
                        if let Tracker::Array {
1339
                            current_child: Some(idx),
19✔
1340
                            ..
1341
                        } = &frame.tracker
26✔
1342
                        {
19✔
1343
                            path_components.push(format!("[{idx}]"));
19✔
1344
                        }
19✔
1345
                    }
1346
                    // You can add more for Slice, Vec, etc., if applicable
1347
                    _ => {
×
1348
                        // just indicate "[]" for sequence
×
1349
                        path_components.push("[]".to_string());
×
1350
                    }
×
1351
                },
1352
                Type::Pointer(_) => {
×
1353
                    // Indicate deref
×
1354
                    path_components.push("*".to_string());
×
1355
                }
×
1356
                _ => {
899✔
1357
                    // No structural path
899✔
1358
                }
899✔
1359
            }
1360
        }
1361
        // Merge the path_components into a single string
1362
        for component in path_components {
7,439✔
1363
            out.push_str(&component);
7,439✔
1364
        }
7,439✔
1365
        out
3,165✔
1366
    }
3,165✔
1367

1368
    /// Get the field for the parent frame
1369
    pub fn parent_field(&self) -> Option<&Field> {
235✔
1370
        self.frames()
235✔
1371
            .iter()
235✔
1372
            .rev()
235✔
1373
            .nth(1)
235✔
1374
            .and_then(|f| f.get_field())
235✔
1375
    }
235✔
1376

1377
    /// Gets the field for the current frame
1378
    pub fn current_field(&self) -> Option<&Field> {
×
1379
        self.frames().last().and_then(|f| f.get_field())
×
1380
    }
×
1381
}
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