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

facet-rs / facet / 20071389342

09 Dec 2025 04:48PM UTC coverage: 58.627% (-0.1%) from 58.736%
20071389342

push

github

fasterthanlime
feat: add try_from for &str to Utf8PathBuf, Url, Uuid, Ulid

Enable default values from string literals for these common CLI types.
Previously they only supported String as source, now they also accept &str.

4 of 44 new or added lines in 3 files covered. (9.09%)

275 existing lines in 13 files now uncovered.

26243 of 44763 relevant lines covered (58.63%)

633.18 hits per line

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

76.31
/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 {
39,012✔
37
        if self.state != PartialState::Active {
39,012✔
38
            panic!(
×
39
                "Partial::shape() called on non-active Partial (state: {:?})",
40
                self.state
41
            );
42
        }
39,012✔
43
        self.frames()
39,012✔
44
            .last()
39,012✔
45
            .expect("Partial::shape() called but no frames exist - this is a bug")
39,012✔
46
            .shape
39,012✔
47
    }
39,012✔
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✔
218
                        let parent_index = start_depth.saturating_sub(1);
×
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 {
223
                        let parent_frame_index = start_depth + parent_path.len() - 1;
×
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✔
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
                && let Type::User(UserType::Struct(struct_type)) = frame.shape.ty
×
292
            {
×
293
                frame.tracker = Tracker::Struct {
×
294
                    iset: ISet::new(struct_type.fields.len()),
×
295
                    current_child: None,
×
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;
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
                && let Ok(layout) = inner_frame.shape.layout.sized_layout()
67✔
332
                && layout.size() > 0
67✔
333
            {
334
                unsafe {
67✔
335
                    ::alloc::alloc::dealloc(inner_frame.data.as_mut_byte_ptr(), layout);
67✔
336
                }
67✔
337
            }
×
338

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

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

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

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

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

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

406
                return Ok(self);
13✔
407
            }
1✔
408
        }
21,422✔
409

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

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

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

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

477
        if requires_full_init {
21,422✔
478
            let frame = self.frames().last().unwrap();
20,381✔
479
            crate::trace!(
480
                "end(): Checking full init for {}, tracker={:?}, is_init={}",
481
                frame.shape,
482
                frame.tracker.kind(),
483
                frame.is_init
484
            );
485
            let result = frame.require_full_initialization();
20,381✔
486
            crate::trace!(
487
                "end(): require_full_initialization result: {:?}",
488
                result.is_ok()
489
            );
490
            result?
20,381✔
491
        }
1,041✔
492

493
        // Pop the frame and save its data pointer for SmartPointer handling
494
        let mut popped_frame = self.frames_mut().pop().unwrap();
21,417✔
495

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

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

523
                // Store the frame at the current path
524
                let path = current_path.clone();
933✔
525
                stored_frames.insert(path, popped_frame);
933✔
526

527
                // Pop from current_path
528
                current_path.pop();
933✔
529

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

535
                return Ok(self);
933✔
536
            }
303✔
537
        }
20,181✔
538

539
        // check if this needs deserialization from a different shape
540
        if popped_frame.using_custom_deserialization {
20,484✔
541
            // Check for field-level proxy first, then fall back to shape-level proxy
542
            let deserialize_with: Option<facet_core::ProxyConvertInFn> = self
88✔
543
                .parent_field()
88✔
544
                .and_then(|f| f.proxy().map(|p| p.convert_in));
88✔
545

546
            // Fall back to shape-level proxy stored in the frame
547
            let deserialize_with =
88✔
548
                deserialize_with.or_else(|| popped_frame.shape_level_proxy.map(|p| p.convert_in));
88✔
549

550
            if let Some(deserialize_with) = deserialize_with {
88✔
551
                let parent_frame = self.frames_mut().last_mut().unwrap();
88✔
552

553
                trace!(
554
                    "Detected custom conversion needed from {} to {}",
555
                    popped_frame.shape, parent_frame.shape
556
                );
557

558
                unsafe {
559
                    let res = {
88✔
560
                        let inner_value_ptr = popped_frame.data.assume_init().as_const();
88✔
561
                        (deserialize_with)(inner_value_ptr, parent_frame.data)
88✔
562
                    };
563
                    let popped_frame_shape = popped_frame.shape;
88✔
564

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

589
        // Update parent frame's tracking when popping from a child
590
        let parent_frame = self.frames_mut().last_mut().unwrap();
20,396✔
591

592
        crate::trace!(
593
            "end(): Popped {} (tracker {:?}), Parent {} (tracker {:?})",
594
            popped_frame.shape,
595
            popped_frame.tracker.kind(),
596
            parent_frame.shape,
597
            parent_frame.tracker.kind()
598
        );
599

600
        // Check if we need to do a conversion - this happens when:
601
        // 1. The parent frame has an inner type that matches the popped frame's shape
602
        // 2. The parent frame has try_from
603
        // 3. The parent frame is not yet initialized
604
        // 4. The parent frame's tracker is Scalar (not Option, SmartPointer, etc.)
605
        //    This ensures we only do conversion when begin_inner was used, not begin_some
606
        let needs_conversion = !parent_frame.is_init
20,396✔
607
            && matches!(parent_frame.tracker, Tracker::Scalar)
5,185✔
608
            && parent_frame.shape.inner.is_some()
17✔
609
            && parent_frame.shape.inner.unwrap() == popped_frame.shape
17✔
610
            && match parent_frame.shape.vtable {
9✔
611
                facet_core::VTableErased::Direct(vt) => vt.try_from.is_some(),
8✔
612
                facet_core::VTableErased::Indirect(vt) => vt.try_from.is_some(),
1✔
613
            };
614

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

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

641
            // Perform the conversion
642
            let inner_ptr = unsafe { popped_frame.data.assume_init().as_const() };
9✔
643
            let inner_shape = popped_frame.shape;
9✔
644

645
            trace!("Converting from {} to {}", inner_shape, parent_frame.shape);
646

647
            // Handle Direct and Indirect vtables differently due to different return types
648
            let result = match parent_frame.shape.vtable {
9✔
649
                facet_core::VTableErased::Direct(vt) => {
8✔
650
                    if let Some(try_from_fn) = vt.try_from {
8✔
651
                        unsafe {
652
                            try_from_fn(
8✔
653
                                parent_frame.data.as_mut_byte_ptr() as *mut (),
8✔
654
                                inner_shape,
8✔
655
                                inner_ptr,
8✔
656
                            )
8✔
657
                        }
658
                    } else {
659
                        return Err(ReflectError::OperationFailed {
×
660
                            shape: parent_frame.shape,
×
661
                            operation: "try_from not available for this type",
×
UNCOV
662
                        });
×
663
                    }
664
                }
665
                facet_core::VTableErased::Indirect(vt) => {
1✔
666
                    if let Some(try_from_fn) = vt.try_from {
1✔
667
                        let ox_mut = facet_core::OxMut::new(
1✔
668
                            unsafe { parent_frame.data.assume_init() },
1✔
669
                            parent_frame.shape,
1✔
670
                        );
671
                        match unsafe { try_from_fn(ox_mut.into(), inner_shape, inner_ptr) } {
1✔
672
                            Some(result) => result,
1✔
673
                            None => {
674
                                return Err(ReflectError::OperationFailed {
×
675
                                    shape: parent_frame.shape,
×
676
                                    operation: "try_from not available for inner type",
×
677
                                });
×
678
                            }
679
                        }
680
                    } else {
681
                        return Err(ReflectError::OperationFailed {
×
682
                            shape: parent_frame.shape,
×
683
                            operation: "try_from not available for this type",
×
684
                        });
×
685
                    }
686
                }
687
            };
688

689
            if let Err(e) = result {
9✔
690
                trace!("Conversion failed: {e:?}");
691

692
                // Deallocate the inner value's memory since conversion failed
693
                if let FrameOwnership::Owned = popped_frame.ownership
2✔
694
                    && let Ok(layout) = popped_frame.shape.layout.sized_layout()
2✔
695
                    && layout.size() > 0
2✔
696
                {
697
                    trace!(
698
                        "Deallocating conversion frame memory after failure: size={}, align={}",
699
                        layout.size(),
700
                        layout.align()
701
                    );
702
                    unsafe {
2✔
703
                        ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
2✔
704
                    }
2✔
705
                }
×
706

707
                return Err(ReflectError::TryFromError {
2✔
708
                    src_shape: inner_shape,
2✔
709
                    dst_shape: parent_frame.shape,
2✔
710
                    inner: facet_core::TryFromError::Generic(e),
2✔
711
                });
2✔
712
            }
7✔
713

714
            trace!("Conversion succeeded, marking parent as initialized");
715
            parent_frame.is_init = true;
7✔
716

717
            // Deallocate the inner value's memory since try_from consumed it
718
            if let FrameOwnership::Owned = popped_frame.ownership
7✔
719
                && let Ok(layout) = popped_frame.shape.layout.sized_layout()
7✔
720
                && layout.size() > 0
7✔
721
            {
722
                trace!(
723
                    "Deallocating conversion frame memory: size={}, align={}",
724
                    layout.size(),
725
                    layout.align()
726
                );
727
                unsafe {
7✔
728
                    ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
7✔
729
                }
7✔
730
            }
×
731

732
            return Ok(self);
7✔
733
        }
20,387✔
734

735
        // For Field-owned frames, reclaim responsibility in parent's tracker
736
        // Only mark as initialized if the child frame was actually initialized.
737
        // This prevents double-free when begin_inner/begin_some drops a value via
738
        // prepare_for_reinitialization but then fails, leaving the child uninitialized.
739
        //
740
        // We use require_full_initialization() rather than just is_init because:
741
        // - Scalar frames use is_init as the source of truth
742
        // - Struct/Array/Enum frames use their iset/data as the source of truth
743
        //   (is_init may never be set to true for these tracker types)
744
        if let FrameOwnership::Field { field_idx } = popped_frame.ownership {
20,387✔
745
            let child_is_initialized = popped_frame.require_full_initialization().is_ok();
4,694✔
746
            match &mut parent_frame.tracker {
4,694✔
747
                Tracker::Struct {
748
                    iset,
4,023✔
749
                    current_child,
4,023✔
750
                } => {
751
                    if child_is_initialized {
4,023✔
752
                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
4,023✔
753
                    }
4,023✔
754
                    *current_child = None;
4,023✔
755
                }
756
                Tracker::Array {
757
                    iset,
145✔
758
                    current_child,
145✔
759
                } => {
760
                    if child_is_initialized {
145✔
761
                        iset.set(field_idx); // Parent reclaims responsibility only if child was init
145✔
762
                    }
145✔
763
                    *current_child = None;
145✔
764
                }
765
                Tracker::Enum {
766
                    data,
526✔
767
                    current_child,
526✔
768
                    ..
769
                } => {
770
                    if child_is_initialized {
526✔
771
                        data.set(field_idx); // Parent reclaims responsibility only if child was init
526✔
772
                    }
526✔
773
                    *current_child = None;
526✔
774
                }
775
                _ => {}
×
776
            }
777
            return Ok(self);
4,694✔
778
        }
15,693✔
779

780
        match &mut parent_frame.tracker {
12,897✔
781
            Tracker::SmartPointer => {
782
                // We just popped the inner value frame, so now we need to create the smart pointer
783
                if let Def::Pointer(smart_ptr_def) = parent_frame.shape.def {
146✔
784
                    // The inner value must be fully initialized before we can create the smart pointer
785
                    if let Err(e) = popped_frame.require_full_initialization() {
146✔
786
                        // Inner value wasn't initialized, deallocate and return error
787
                        popped_frame.deinit();
×
788
                        popped_frame.dealloc();
×
789
                        return Err(e);
×
790
                    }
146✔
791

792
                    let Some(new_into_fn) = smart_ptr_def.vtable.new_into_fn else {
146✔
793
                        popped_frame.deinit();
×
794
                        popped_frame.dealloc();
×
795
                        return Err(ReflectError::OperationFailed {
×
796
                            shape: parent_frame.shape,
×
797
                            operation: "SmartPointer missing new_into_fn",
×
798
                        });
×
799
                    };
800

801
                    // The child frame contained the inner value
802
                    let inner_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
146✔
803

804
                    // Use new_into_fn to create the Box
805
                    unsafe {
146✔
806
                        new_into_fn(parent_frame.data, inner_ptr);
146✔
807
                    }
146✔
808

809
                    // We just moved out of it
810
                    popped_frame.tracker = Tracker::Scalar;
146✔
811
                    popped_frame.is_init = false;
146✔
812

813
                    // Deallocate the inner value's memory since new_into_fn moved it
814
                    popped_frame.dealloc();
146✔
815

816
                    parent_frame.is_init = true;
146✔
817
                }
×
818
            }
819
            Tracker::List { current_child } if parent_frame.is_init => {
12,897✔
820
                if *current_child {
12,897✔
821
                    // We just popped an element frame, now push it to the list
822
                    if let Def::List(list_def) = parent_frame.shape.def {
12,897✔
823
                        let Some(push_fn) = list_def.push() else {
12,897✔
824
                            return Err(ReflectError::OperationFailed {
×
825
                                shape: parent_frame.shape,
×
826
                                operation: "List missing push function",
×
827
                            });
×
828
                        };
829

830
                        // The child frame contained the element value
831
                        let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
12,897✔
832

833
                        // Use push to add element to the list
834
                        unsafe {
12,897✔
835
                            push_fn(
12,897✔
836
                                PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
12,897✔
837
                                element_ptr,
12,897✔
838
                            );
12,897✔
839
                        }
12,897✔
840

841
                        // Push moved out of popped_frame
842
                        popped_frame.tracker = Tracker::Scalar;
12,897✔
843
                        popped_frame.is_init = false;
12,897✔
844
                        popped_frame.dealloc();
12,897✔
845

846
                        *current_child = false;
12,897✔
847
                    }
×
848
                }
×
849
            }
850
            Tracker::Map { insert_state } if parent_frame.is_init => {
2,224✔
851
                match insert_state {
2,224✔
852
                    MapInsertState::PushingKey { key_ptr, .. } => {
1,115✔
853
                        // We just popped the key frame - mark key as initialized and transition
1,115✔
854
                        // to PushingValue state
1,115✔
855
                        *insert_state = MapInsertState::PushingValue {
1,115✔
856
                            key_ptr: *key_ptr,
1,115✔
857
                            value_ptr: None,
1,115✔
858
                            value_initialized: false,
1,115✔
859
                        };
1,115✔
860
                    }
1,115✔
861
                    MapInsertState::PushingValue {
862
                        key_ptr, value_ptr, ..
1,109✔
863
                    } => {
864
                        // We just popped the value frame, now insert the pair
865
                        if let (Some(value_ptr), Def::Map(map_def)) =
1,109✔
866
                            (value_ptr, parent_frame.shape.def)
1,109✔
867
                        {
868
                            let insert = map_def.vtable.insert;
1,109✔
869

870
                            // Use insert to add key-value pair to the map
871
                            unsafe {
1,109✔
872
                                insert(
1,109✔
873
                                    PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1,109✔
874
                                    PtrMut::new(key_ptr.as_mut_byte_ptr()),
1,109✔
875
                                    PtrMut::new(value_ptr.as_mut_byte_ptr()),
1,109✔
876
                                );
1,109✔
877
                            }
1,109✔
878

879
                            // Note: We don't deallocate the key and value memory here.
880
                            // The insert function has semantically moved the values into the map,
881
                            // but we still need to deallocate the temporary buffers.
882
                            // However, since we don't have frames for them anymore (they were popped),
883
                            // we need to handle deallocation here.
884
                            if let Ok(key_shape) = map_def.k().layout.sized_layout()
1,109✔
885
                                && key_shape.size() > 0
1,109✔
886
                            {
887
                                unsafe {
1,109✔
888
                                    ::alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_shape);
1,109✔
889
                                }
1,109✔
890
                            }
×
891
                            if let Ok(value_shape) = map_def.v().layout.sized_layout()
1,109✔
892
                                && value_shape.size() > 0
1,109✔
893
                            {
894
                                unsafe {
1,109✔
895
                                    ::alloc::alloc::dealloc(
1,109✔
896
                                        value_ptr.as_mut_byte_ptr(),
1,109✔
897
                                        value_shape,
1,109✔
898
                                    );
1,109✔
899
                                }
1,109✔
900
                            }
×
901

902
                            // Reset to idle state
903
                            *insert_state = MapInsertState::Idle;
1,109✔
904
                        }
×
905
                    }
906
                    MapInsertState::Idle => {
×
907
                        // Nothing to do
×
908
                    }
×
909
                }
910
            }
911
            Tracker::Set { current_child } if parent_frame.is_init => {
42✔
912
                if *current_child {
42✔
913
                    // We just popped an element frame, now insert it into the set
914
                    if let Def::Set(set_def) = parent_frame.shape.def {
42✔
915
                        let insert = set_def.vtable.insert;
42✔
916

42✔
917
                        // The child frame contained the element value
42✔
918
                        let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
42✔
919

42✔
920
                        // Use insert to add element to the set
42✔
921
                        unsafe {
42✔
922
                            insert(
42✔
923
                                PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
42✔
924
                                element_ptr,
42✔
925
                            );
42✔
926
                        }
42✔
927

42✔
928
                        // Insert moved out of popped_frame
42✔
929
                        popped_frame.tracker = Tracker::Scalar;
42✔
930
                        popped_frame.is_init = false;
42✔
931
                        popped_frame.dealloc();
42✔
932

42✔
933
                        *current_child = false;
42✔
934
                    }
42✔
935
                }
×
936
            }
937
            Tracker::Option { building_inner } => {
289✔
938
                crate::trace!(
939
                    "end(): matched Tracker::Option, building_inner={}",
940
                    *building_inner
941
                );
942
                // We just popped the inner value frame for an Option's Some variant
943
                if *building_inner {
289✔
944
                    if let Def::Option(option_def) = parent_frame.shape.def {
289✔
945
                        // Use the Option vtable to initialize Some(inner_value)
946
                        let init_some_fn = option_def.vtable.init_some;
289✔
947

948
                        // The popped frame contains the inner value
949
                        let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
289✔
950

951
                        // Initialize the Option as Some(inner_value)
952
                        unsafe {
289✔
953
                            init_some_fn(parent_frame.data, inner_value_ptr);
289✔
954
                        }
289✔
955

956
                        // Deallocate the inner value's memory since init_some_fn moved it
957
                        if let FrameOwnership::Owned = popped_frame.ownership
289✔
958
                            && let Ok(layout) = popped_frame.shape.layout.sized_layout()
289✔
959
                            && layout.size() > 0
289✔
960
                        {
961
                            unsafe {
289✔
962
                                ::alloc::alloc::dealloc(
289✔
963
                                    popped_frame.data.as_mut_byte_ptr(),
289✔
964
                                    layout,
289✔
965
                                );
289✔
966
                            }
289✔
967
                        }
×
968

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

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

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

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

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

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

1088
                        popped_frame.require_full_initialization()?;
8✔
1089

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

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

1104
                        parent_frame.deinit();
8✔
1105

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

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

1146
                        parent_frame.is_init = true;
8✔
1147

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

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

1178
                    popped_frame.tracker = Tracker::Scalar;
33✔
1179
                    popped_frame.is_init = false;
33✔
1180
                    popped_frame.dealloc();
33✔
1181

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

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

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

22✔
1218
                        // Push moved out of popped_frame
22✔
1219
                        popped_frame.tracker = Tracker::Scalar;
22✔
1220
                        popped_frame.is_init = false;
22✔
1221
                        popped_frame.dealloc();
22✔
1222

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

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

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

26✔
1255
                        // Insert moved out of popped_frame
26✔
1256
                        popped_frame.tracker = Tracker::Scalar;
26✔
1257
                        popped_frame.is_init = false;
26✔
1258
                        popped_frame.dealloc();
26✔
1259

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

1268
        Ok(self)
15,693✔
1269
    }
21,436✔
1270

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

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

1364
    /// Get the field for the parent frame
1365
    pub fn parent_field(&self) -> Option<&Field> {
292✔
1366
        self.frames()
292✔
1367
            .iter()
292✔
1368
            .rev()
292✔
1369
            .nth(1)
292✔
1370
            .and_then(|f| f.get_field())
292✔
1371
    }
292✔
1372

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