facet_reflect/partial/partial_api/misc.rs
1use super::*;
2
3////////////////////////////////////////////////////////////////////////////////////////////////////
4// Misc.
5////////////////////////////////////////////////////////////////////////////////////////////////////
6impl<'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 {
26 self.frames().len()
27 }
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 {
37 if self.state != PartialState::Active {
38 panic!(
39 "Partial::shape() called on non-active Partial (state: {:?})",
40 self.state
41 );
42 }
43 self.frames()
44 .last()
45 .expect("Partial::shape() called but no frames exist - this is a bug")
46 .allocated
47 .shape()
48 }
49
50 /// Returns the shape of the current frame, or `None` if the Partial is
51 /// inactive (poisoned or built) or has no frames.
52 ///
53 /// This is useful for debugging/logging where you want to inspect the state
54 /// without risking a panic.
55 #[inline]
56 pub fn try_shape(&self) -> Option<&'static Shape> {
57 if self.state != PartialState::Active {
58 return None;
59 }
60 self.frames().last().map(|f| f.allocated.shape())
61 }
62
63 /// Returns true if the current frame is building a smart pointer slice (Arc<\[T\]>, Rc<\[T\]>, Box<\[T\]>).
64 ///
65 /// This is used by deserializers to determine if they should deserialize as a list
66 /// rather than recursing into the smart pointer type.
67 #[inline]
68 pub fn is_building_smart_ptr_slice(&self) -> bool {
69 if self.state != PartialState::Active {
70 return false;
71 }
72 self.frames()
73 .last()
74 .is_some_and(|f| matches!(f.tracker, Tracker::SmartPointerSlice { .. }))
75 }
76
77 /// Returns the current deferred resolution, if in deferred mode.
78 #[inline]
79 pub fn deferred_resolution(&self) -> Option<&Resolution> {
80 self.resolution()
81 }
82
83 /// Returns the current path in deferred mode as a slice (for debugging/tracing).
84 #[inline]
85 pub fn current_path_slice(&self) -> Option<&[&'static str]> {
86 self.current_path().map(|p| p.as_slice())
87 }
88
89 /// Enables deferred materialization mode with the given Resolution.
90 ///
91 /// When deferred mode is enabled:
92 /// - `end()` stores frames instead of validating them
93 /// - Re-entering a path restores the stored frame with its state intact
94 /// - `finish_deferred()` performs final validation and materialization
95 ///
96 /// This allows deserializers to handle interleaved fields (e.g., TOML dotted
97 /// keys, flattened structs) where nested fields aren't contiguous in the input.
98 ///
99 /// # Use Cases
100 ///
101 /// - TOML dotted keys: `inner.x = 1` followed by `count = 2` then `inner.y = 3`
102 /// - Flattened structs where nested fields appear at the parent level
103 /// - Any format where field order doesn't match struct nesting
104 ///
105 /// # Errors
106 ///
107 /// Returns an error if already in deferred mode.
108 #[inline]
109 pub fn begin_deferred(mut self, resolution: Resolution) -> Result<Self, ReflectError> {
110 // Cannot enable deferred mode if already in deferred mode
111 if self.is_deferred() {
112 return Err(ReflectError::InvariantViolation {
113 invariant: "begin_deferred() called but already in deferred mode",
114 });
115 }
116
117 // Take the stack out of Strict mode and wrap in Deferred mode
118 let FrameMode::Strict { stack } = core::mem::replace(
119 &mut self.mode,
120 FrameMode::Strict { stack: Vec::new() }, // temporary placeholder
121 ) else {
122 unreachable!("just checked we're not in deferred mode");
123 };
124
125 let start_depth = stack.len();
126 self.mode = FrameMode::Deferred {
127 stack,
128 resolution,
129 start_depth,
130 current_path: Vec::new(),
131 stored_frames: BTreeMap::new(),
132 };
133 Ok(self)
134 }
135
136 /// Finishes deferred mode: validates all stored frames and finalizes.
137 ///
138 /// This method:
139 /// 1. Validates that all stored frames are fully initialized
140 /// 2. Processes frames from deepest to shallowest, updating parent ISets
141 /// 3. Validates the root frame
142 ///
143 /// # Errors
144 ///
145 /// Returns an error if any required fields are missing or if the partial is
146 /// not in deferred mode.
147 pub fn finish_deferred(mut self) -> Result<Self, ReflectError> {
148 // Check if we're in deferred mode first, before extracting state
149 if !self.is_deferred() {
150 return Err(ReflectError::InvariantViolation {
151 invariant: "finish_deferred() called but deferred mode is not enabled",
152 });
153 }
154
155 // Extract deferred state, transitioning back to Strict mode
156 let FrameMode::Deferred {
157 stack,
158 start_depth,
159 mut stored_frames,
160 ..
161 } = core::mem::replace(&mut self.mode, FrameMode::Strict { stack: Vec::new() })
162 else {
163 unreachable!("just checked is_deferred()");
164 };
165
166 // Restore the stack to self.mode
167 self.mode = FrameMode::Strict { stack };
168
169 // Sort paths by depth (deepest first) so we process children before parents
170 let mut paths: Vec<_> = stored_frames.keys().cloned().collect();
171 paths.sort_by_key(|b| core::cmp::Reverse(b.len()));
172
173 trace!(
174 "finish_deferred: Processing {} stored frames in order: {:?}",
175 paths.len(),
176 paths
177 );
178
179 // Process each stored frame from deepest to shallowest
180 for path in paths {
181 let mut frame = stored_frames.remove(&path).unwrap();
182
183 trace!(
184 "finish_deferred: Processing frame at {:?}, shape {}, tracker {:?}",
185 path,
186 frame.allocated.shape(),
187 frame.tracker.kind()
188 );
189
190 // Fill in defaults for unset fields that have defaults
191 if let Err(e) = frame.fill_defaults() {
192 frame.deinit();
193 frame.dealloc();
194 for (_, mut remaining_frame) in stored_frames {
195 remaining_frame.deinit();
196 remaining_frame.dealloc();
197 }
198 return Err(e);
199 }
200
201 // Validate the frame is fully initialized
202 if let Err(e) = frame.require_full_initialization() {
203 frame.deinit();
204 frame.dealloc();
205 for (_, mut remaining_frame) in stored_frames {
206 remaining_frame.deinit();
207 remaining_frame.dealloc();
208 }
209 return Err(e);
210 }
211
212 // Update parent's ISet to mark this field as initialized.
213 // The parent could be:
214 // 1. On the frames stack (if path.len() == 1, parent is at start_depth - 1)
215 // 2. On the frames stack (if parent was pushed but never ended)
216 // 3. In stored_frames (if parent was ended during deferred mode)
217 if let Some(field_name) = path.last() {
218 let parent_path: Vec<_> = path[..path.len() - 1].to_vec();
219
220 // Special handling for Option inner values: when path ends with "Some",
221 // the parent is an Option frame and we need to complete the Option by
222 // writing the inner value into the Option's memory.
223 if *field_name == "Some" {
224 // Find the Option frame (parent)
225 let option_frame = if parent_path.is_empty() {
226 let parent_index = start_depth.saturating_sub(1);
227 self.frames_mut().get_mut(parent_index)
228 } else if let Some(parent_frame) = stored_frames.get_mut(&parent_path) {
229 Some(parent_frame)
230 } else {
231 let parent_frame_index = start_depth + parent_path.len() - 1;
232 self.frames_mut().get_mut(parent_frame_index)
233 };
234
235 if let Some(option_frame) = option_frame {
236 // The frame contains the inner value - write it into the Option's memory
237 Self::complete_option_frame(option_frame, frame);
238 // Frame data has been transferred to Option - don't drop it
239 continue;
240 }
241 }
242
243 if parent_path.is_empty() {
244 // Parent is the frame that was current when deferred mode started.
245 // It's at index (start_depth - 1) because deferred mode stores frames
246 // relative to the position at start_depth.
247 let parent_index = start_depth.saturating_sub(1);
248 if let Some(root_frame) = self.frames_mut().get_mut(parent_index) {
249 Self::mark_field_initialized(root_frame, field_name);
250 }
251 } else {
252 // Try stored_frames first
253 if let Some(parent_frame) = stored_frames.get_mut(&parent_path) {
254 Self::mark_field_initialized(parent_frame, field_name);
255 } else {
256 // Parent might still be on the frames stack (never ended).
257 // The frame at index (start_depth + parent_path.len() - 1) should be the parent.
258 let parent_frame_index = start_depth + parent_path.len() - 1;
259 if let Some(parent_frame) = self.frames_mut().get_mut(parent_frame_index) {
260 Self::mark_field_initialized(parent_frame, field_name);
261 }
262 }
263 }
264 }
265
266 // Frame is validated and parent is updated - dealloc if needed
267 frame.dealloc();
268 }
269
270 // Invariant check: we must have at least one frame after finish_deferred
271 if self.frames().is_empty() {
272 // No need to poison - returning Err consumes self, Drop will handle cleanup
273 return Err(ReflectError::InvariantViolation {
274 invariant: "finish_deferred() left Partial with no frames",
275 });
276 }
277
278 // Fill defaults and validate the root frame is fully initialized
279 if let Some(frame) = self.frames_mut().last_mut() {
280 // Fill defaults - this can fail if a field has #[facet(default)] but no default impl
281 frame.fill_defaults()?;
282 // Root validation failed. At this point, all stored frames have been
283 // processed and their parent isets updated.
284 // No need to poison - returning Err consumes self, Drop will handle cleanup
285 frame.require_full_initialization()?;
286 }
287
288 Ok(self)
289 }
290
291 /// Mark a field as initialized in a frame's tracker
292 fn mark_field_initialized(frame: &mut Frame, field_name: &str) {
293 if let Some(idx) = Self::find_field_index(frame, field_name) {
294 // If the tracker is Scalar but this is a struct type, upgrade to Struct tracker.
295 // This can happen if the frame was deinit'd (e.g., by a failed set_default)
296 // which resets the tracker to Scalar.
297 if matches!(frame.tracker, Tracker::Scalar)
298 && let Type::User(UserType::Struct(struct_type)) = frame.allocated.shape().ty
299 {
300 frame.tracker = Tracker::Struct {
301 iset: ISet::new(struct_type.fields.len()),
302 current_child: None,
303 };
304 }
305
306 match &mut frame.tracker {
307 Tracker::Struct { iset, .. } => {
308 iset.set(idx);
309 }
310 Tracker::Enum { data, .. } => {
311 data.set(idx);
312 }
313 Tracker::Array { iset, .. } => {
314 iset.set(idx);
315 }
316 _ => {}
317 }
318 }
319 }
320
321 /// Complete an Option frame by writing the inner value and marking it initialized.
322 /// Used in finish_deferred when processing a stored frame at a path ending with "Some".
323 fn complete_option_frame(option_frame: &mut Frame, inner_frame: Frame) {
324 if let Def::Option(option_def) = option_frame.allocated.shape().def {
325 // Use the Option vtable to initialize Some(inner_value)
326 let init_some_fn = option_def.vtable.init_some;
327
328 // The inner frame contains the inner value
329 let inner_value_ptr = unsafe { inner_frame.data.assume_init().as_const() };
330
331 // Initialize the Option as Some(inner_value)
332 unsafe {
333 init_some_fn(option_frame.data, inner_value_ptr);
334 }
335
336 // Deallocate the inner value's memory since init_some_fn moved it
337 if let FrameOwnership::Owned = inner_frame.ownership
338 && let Ok(layout) = inner_frame.allocated.shape().layout.sized_layout()
339 && layout.size() > 0
340 {
341 unsafe {
342 ::alloc::alloc::dealloc(inner_frame.data.as_mut_byte_ptr(), layout);
343 }
344 }
345
346 // Mark the Option as initialized
347 option_frame.tracker = Tracker::Option {
348 building_inner: false,
349 };
350 option_frame.is_init = true;
351 }
352 }
353
354 /// Find the field index for a given field name in a frame
355 fn find_field_index(frame: &Frame, field_name: &str) -> Option<usize> {
356 match frame.allocated.shape().ty {
357 Type::User(UserType::Struct(struct_type)) => {
358 struct_type.fields.iter().position(|f| f.name == field_name)
359 }
360 Type::User(UserType::Enum(_)) => {
361 if let Tracker::Enum { variant, .. } = &frame.tracker {
362 variant
363 .data
364 .fields
365 .iter()
366 .position(|f| f.name == field_name)
367 } else {
368 None
369 }
370 }
371 _ => None,
372 }
373 }
374
375 /// Pops the current frame off the stack, indicating we're done initializing the current field
376 pub fn end(mut self) -> Result<Self, ReflectError> {
377 if let Some(_frame) = self.frames().last() {
378 crate::trace!(
379 "end() called: shape={}, tracker={:?}, is_init={}",
380 _frame.allocated.shape(),
381 _frame.tracker.kind(),
382 _frame.is_init
383 );
384 }
385
386 // Special handling for SmartPointerSlice - convert builder to Arc
387 // Check if the current (top) frame is a SmartPointerSlice that needs conversion
388 let needs_slice_conversion = {
389 let frames = self.frames();
390 if frames.is_empty() {
391 false
392 } else {
393 let top_idx = frames.len() - 1;
394 crate::trace!(
395 "end(): frames.len()={}, top tracker={:?}",
396 frames.len(),
397 frames[top_idx].tracker.kind()
398 );
399 matches!(
400 frames[top_idx].tracker,
401 Tracker::SmartPointerSlice {
402 building_item: false,
403 ..
404 }
405 )
406 }
407 };
408
409 crate::trace!("end(): needs_slice_conversion={}", needs_slice_conversion);
410
411 if needs_slice_conversion {
412 let frames = self.frames_mut();
413 let top_idx = frames.len() - 1;
414
415 if let Tracker::SmartPointerSlice { vtable, .. } = &frames[top_idx].tracker {
416 // Convert the builder to Arc<[T]>
417 let vtable = *vtable;
418 let builder_ptr = unsafe { frames[top_idx].data.assume_init() };
419 let arc_ptr = unsafe { (vtable.convert_fn)(builder_ptr) };
420
421 match frames[top_idx].ownership {
422 FrameOwnership::Field { field_idx } => {
423 // Arc<[T]> is a field in a struct
424 // The field frame's original data pointer was overwritten with the builder pointer,
425 // so we need to reconstruct where the Arc should be written.
426
427 // Get parent frame and field info
428 let parent_idx = top_idx - 1;
429 let parent_frame = &frames[parent_idx];
430 let current_shape = frames[top_idx].allocated.shape();
431
432 // Get the field to find its offset
433 let field = if let Type::User(UserType::Struct(struct_type)) =
434 parent_frame.allocated.shape().ty
435 {
436 &struct_type.fields[field_idx]
437 } else {
438 return Err(ReflectError::InvariantViolation {
439 invariant: "SmartPointerSlice field frame parent must be a struct",
440 });
441 };
442
443 // Calculate where the Arc should be written (parent.data + field.offset)
444 let field_location =
445 unsafe { parent_frame.data.field_uninit(field.offset) };
446
447 // Write the Arc to the parent struct's field location
448 let arc_layout = current_shape.layout.sized_layout().map_err(|_| {
449 ReflectError::Unsized {
450 shape: current_shape,
451 operation: "SmartPointerSlice conversion requires sized Arc",
452 }
453 })?;
454 let arc_size = arc_layout.size();
455 unsafe {
456 core::ptr::copy_nonoverlapping(
457 arc_ptr.as_byte_ptr(),
458 field_location.as_mut_byte_ptr(),
459 arc_size,
460 );
461 }
462
463 // Free the staging allocation from convert_fn (the Arc was copied to field_location)
464 unsafe {
465 ::alloc::alloc::dealloc(arc_ptr.as_byte_ptr() as *mut u8, arc_layout);
466 }
467
468 // Update the frame to point to the correct field location and mark as initialized
469 frames[top_idx].data = field_location;
470 frames[top_idx].tracker = Tracker::Scalar;
471 frames[top_idx].is_init = true;
472
473 // Return WITHOUT popping - the field frame will be popped by the next end() call
474 return Ok(self);
475 }
476 FrameOwnership::Owned => {
477 // Arc<[T]> is the root type or owned independently
478 // The frame already has the allocation, we just need to update it with the Arc
479
480 // The frame's data pointer is currently the builder, but we allocated
481 // the Arc memory in the convert_fn. Update to point to the Arc.
482 frames[top_idx].data = PtrUninit::new(arc_ptr.as_byte_ptr() as *mut u8);
483 frames[top_idx].tracker = Tracker::Scalar;
484 frames[top_idx].is_init = true;
485 // Keep Owned ownership so Guard will properly deallocate
486
487 // Return WITHOUT popping - the frame stays and will be built/dropped normally
488 return Ok(self);
489 }
490 FrameOwnership::TrackedBuffer | FrameOwnership::BorrowedInPlace => {
491 return Err(ReflectError::InvariantViolation {
492 invariant: "SmartPointerSlice cannot have TrackedBuffer/BorrowedInPlace ownership after conversion",
493 });
494 }
495 }
496 }
497 }
498
499 if self.frames().len() <= 1 {
500 // Never pop the last/root frame - this indicates a broken state machine
501 // No need to poison - returning Err consumes self, Drop will handle cleanup
502 return Err(ReflectError::InvariantViolation {
503 invariant: "Partial::end() called with only one frame on the stack",
504 });
505 }
506
507 // In deferred mode, cannot pop below the start depth
508 if let Some(start_depth) = self.start_depth()
509 && self.frames().len() <= start_depth
510 {
511 // No need to poison - returning Err consumes self, Drop will handle cleanup
512 return Err(ReflectError::InvariantViolation {
513 invariant: "Partial::end() called but would pop below deferred start depth",
514 });
515 }
516
517 // Require that the top frame is fully initialized before popping.
518 // Skip this check in deferred mode - validation happens in finish_deferred().
519 // EXCEPT for collection items (map, list, set, option) which must be fully
520 // initialized before insertion/completion.
521 let requires_full_init = if !self.is_deferred() {
522 true
523 } else {
524 // In deferred mode, first check if this frame will be stored (tracked field).
525 // If so, skip full init check - the frame will be stored for later completion.
526 let is_tracked_frame = if let FrameMode::Deferred {
527 stack,
528 start_depth,
529 current_path,
530 ..
531 } = &self.mode
532 {
533 // Path depth should match the relative frame depth for a tracked field.
534 // frames.len() - start_depth should equal path.len() for tracked fields.
535 let relative_depth = stack.len() - *start_depth;
536 !current_path.is_empty() && current_path.len() == relative_depth
537 } else {
538 false
539 };
540
541 if is_tracked_frame {
542 // This frame will be stored in deferred mode - don't require full init
543 false
544 } else {
545 // Check if parent is a collection that requires fully initialized items
546 if self.frames().len() >= 2 {
547 let frame_len = self.frames().len();
548 let parent_frame = &self.frames()[frame_len - 2];
549 matches!(
550 parent_frame.tracker,
551 Tracker::Map { .. }
552 | Tracker::List { .. }
553 | Tracker::Set { .. }
554 | Tracker::Option { .. }
555 | Tracker::Result { .. }
556 | Tracker::DynamicValue {
557 state: DynamicValueState::Array { .. }
558 }
559 )
560 } else {
561 false
562 }
563 }
564 };
565
566 if requires_full_init {
567 // Fill defaults before checking full initialization
568 // This is important for structs in deferred mode that are children of collections
569 // (lists, maps, etc.) - they must be fully initialized before being inserted,
570 // but defaults should be applied first.
571 if self.is_deferred()
572 && let Some(frame) = self.frames_mut().last_mut()
573 {
574 crate::trace!(
575 "end(): Filling defaults before full init check for {}, tracker={:?}",
576 frame.allocated.shape(),
577 frame.tracker.kind()
578 );
579 frame.fill_defaults()?;
580 }
581
582 let frame = self.frames().last().unwrap();
583 crate::trace!(
584 "end(): Checking full init for {}, tracker={:?}, is_init={}",
585 frame.allocated.shape(),
586 frame.tracker.kind(),
587 frame.is_init
588 );
589 let result = frame.require_full_initialization();
590 crate::trace!(
591 "end(): require_full_initialization result: {:?}",
592 result.is_ok()
593 );
594 result?
595 }
596
597 // Pop the frame and save its data pointer for SmartPointer handling
598 let mut popped_frame = self.frames_mut().pop().unwrap();
599
600 // In deferred mode, store the frame for potential re-entry and skip
601 // the normal parent-updating logic. The frame will be finalized later
602 // in finish_deferred().
603 //
604 // We only store if the path depth matches the frame depth, meaning we're
605 // ending a tracked struct/enum field, not something like begin_some()
606 // or a field inside a collection item.
607 if let FrameMode::Deferred {
608 stack,
609 start_depth,
610 current_path,
611 stored_frames,
612 ..
613 } = &mut self.mode
614 {
615 // Path depth should match the relative frame depth for a tracked field.
616 // After popping: frames.len() - start_depth + 1 should equal path.len()
617 // for fields entered via begin_field (not begin_some/begin_inner).
618 let relative_depth = stack.len() - *start_depth + 1;
619 let is_tracked_field = !current_path.is_empty() && current_path.len() == relative_depth;
620
621 if is_tracked_field {
622 trace!(
623 "end(): Storing frame for deferred path {:?}, shape {}",
624 current_path,
625 popped_frame.allocated.shape()
626 );
627
628 // Store the frame at the current path
629 let path = current_path.clone();
630 stored_frames.insert(path, popped_frame);
631
632 // Pop from current_path
633 current_path.pop();
634
635 // Clear parent's current_child tracking
636 if let Some(parent_frame) = stack.last_mut() {
637 parent_frame.tracker.clear_current_child();
638 }
639
640 return Ok(self);
641 }
642 }
643
644 // check if this needs deserialization from a different shape
645 if popped_frame.using_custom_deserialization {
646 // Check for field-level proxy first, then fall back to shape-level proxy
647 let deserialize_with: Option<facet_core::ProxyConvertInFn> = self
648 .parent_field()
649 .and_then(|f| f.proxy().map(|p| p.convert_in));
650
651 // Fall back to shape-level proxy stored in the frame
652 let deserialize_with =
653 deserialize_with.or_else(|| popped_frame.shape_level_proxy.map(|p| p.convert_in));
654
655 if let Some(deserialize_with) = deserialize_with {
656 let parent_frame = self.frames_mut().last_mut().unwrap();
657
658 trace!(
659 "Detected custom conversion needed from {} to {}",
660 popped_frame.allocated.shape(),
661 parent_frame.allocated.shape()
662 );
663
664 unsafe {
665 let res = {
666 let inner_value_ptr = popped_frame.data.assume_init().as_const();
667 (deserialize_with)(inner_value_ptr, parent_frame.data)
668 };
669 let popped_frame_shape = popped_frame.allocated.shape();
670
671 // Note: We do NOT call deinit() here because deserialize_with uses
672 // ptr::read to take ownership of the source value. Calling deinit()
673 // would cause a double-free. We mark is_init as false to satisfy
674 // dealloc()'s assertion, then deallocate the memory.
675 popped_frame.is_init = false;
676 popped_frame.dealloc();
677 let rptr = res.map_err(|message| ReflectError::CustomDeserializationError {
678 message,
679 src_shape: popped_frame_shape,
680 dst_shape: parent_frame.allocated.shape(),
681 })?;
682 if rptr.as_uninit() != parent_frame.data {
683 return Err(ReflectError::CustomDeserializationError {
684 message: "deserialize_with did not return the expected pointer".into(),
685 src_shape: popped_frame_shape,
686 dst_shape: parent_frame.allocated.shape(),
687 });
688 }
689 parent_frame.mark_as_init();
690 }
691 return Ok(self);
692 }
693 }
694
695 // Update parent frame's tracking when popping from a child
696 // Capture deferred state before borrowing frames mutably
697 let is_deferred = self.is_deferred();
698 let parent_frame = self.frames_mut().last_mut().unwrap();
699
700 crate::trace!(
701 "end(): Popped {} (tracker {:?}), Parent {} (tracker {:?})",
702 popped_frame.allocated.shape(),
703 popped_frame.tracker.kind(),
704 parent_frame.allocated.shape(),
705 parent_frame.tracker.kind()
706 );
707
708 // Check if we need to do a conversion - this happens when:
709 // 1. The parent frame has a builder_shape or inner type that matches the popped frame's shape
710 // 2. The parent frame has try_from
711 // 3. The parent frame is not yet initialized
712 // 4. The parent frame's tracker is Scalar (not Option, SmartPointer, etc.)
713 // This ensures we only do conversion when begin_inner was used, not begin_some
714 let needs_conversion = !parent_frame.is_init
715 && matches!(parent_frame.tracker, Tracker::Scalar)
716 && ((parent_frame.allocated.shape().builder_shape.is_some()
717 && parent_frame.allocated.shape().builder_shape.unwrap()
718 == popped_frame.allocated.shape())
719 || (parent_frame.allocated.shape().inner.is_some()
720 && parent_frame.allocated.shape().inner.unwrap()
721 == popped_frame.allocated.shape()))
722 && match parent_frame.allocated.shape().vtable {
723 facet_core::VTableErased::Direct(vt) => vt.try_from.is_some(),
724 facet_core::VTableErased::Indirect(vt) => vt.try_from.is_some(),
725 };
726
727 if needs_conversion {
728 trace!(
729 "Detected implicit conversion needed from {} to {}",
730 popped_frame.allocated.shape(),
731 parent_frame.allocated.shape()
732 );
733
734 // The conversion requires the source frame to be fully initialized
735 // (we're about to call assume_init() and pass to try_from)
736 if let Err(e) = popped_frame.require_full_initialization() {
737 // Deallocate the memory since the frame wasn't fully initialized
738 if let FrameOwnership::Owned = popped_frame.ownership
739 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
740 && layout.size() > 0
741 {
742 trace!(
743 "Deallocating uninitialized conversion frame memory: size={}, align={}",
744 layout.size(),
745 layout.align()
746 );
747 unsafe {
748 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
749 }
750 }
751 return Err(e);
752 }
753
754 // Perform the conversion
755 let inner_ptr = unsafe { popped_frame.data.assume_init().as_const() };
756 let inner_shape = popped_frame.allocated.shape();
757
758 trace!(
759 "Converting from {} to {}",
760 inner_shape,
761 parent_frame.allocated.shape()
762 );
763
764 // Handle Direct and Indirect vtables differently due to different return types
765 let result = match parent_frame.allocated.shape().vtable {
766 facet_core::VTableErased::Direct(vt) => {
767 if let Some(try_from_fn) = vt.try_from {
768 unsafe {
769 try_from_fn(
770 parent_frame.data.as_mut_byte_ptr() as *mut (),
771 inner_shape,
772 inner_ptr,
773 )
774 }
775 } else {
776 return Err(ReflectError::OperationFailed {
777 shape: parent_frame.allocated.shape(),
778 operation: "try_from not available for this type",
779 });
780 }
781 }
782 facet_core::VTableErased::Indirect(vt) => {
783 if let Some(try_from_fn) = vt.try_from {
784 // SAFETY: assume_init is valid because we're in a state where the
785 // parent frame has been initialized, and the shape matches.
786 let ox_mut = unsafe {
787 facet_core::OxMut::new(
788 parent_frame.data.assume_init(),
789 parent_frame.allocated.shape(),
790 )
791 };
792 match unsafe { try_from_fn(ox_mut.into(), inner_shape, inner_ptr) } {
793 Some(result) => result,
794 None => {
795 return Err(ReflectError::OperationFailed {
796 shape: parent_frame.allocated.shape(),
797 operation: "try_from not available for inner type",
798 });
799 }
800 }
801 } else {
802 return Err(ReflectError::OperationFailed {
803 shape: parent_frame.allocated.shape(),
804 operation: "try_from not available for this type",
805 });
806 }
807 }
808 };
809
810 if let Err(e) = result {
811 trace!("Conversion failed: {e:?}");
812
813 // Deallocate the inner value's memory since conversion failed
814 if let FrameOwnership::Owned = popped_frame.ownership
815 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
816 && layout.size() > 0
817 {
818 trace!(
819 "Deallocating conversion frame memory after failure: size={}, align={}",
820 layout.size(),
821 layout.align()
822 );
823 unsafe {
824 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
825 }
826 }
827
828 return Err(ReflectError::TryFromError {
829 src_shape: inner_shape,
830 dst_shape: parent_frame.allocated.shape(),
831 inner: facet_core::TryFromError::Generic(e),
832 });
833 }
834
835 trace!("Conversion succeeded, marking parent as initialized");
836 parent_frame.is_init = true;
837
838 // Deallocate the inner value's memory since try_from consumed it
839 if let FrameOwnership::Owned = popped_frame.ownership
840 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
841 && layout.size() > 0
842 {
843 trace!(
844 "Deallocating conversion frame memory: size={}, align={}",
845 layout.size(),
846 layout.align()
847 );
848 unsafe {
849 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
850 }
851 }
852
853 return Ok(self);
854 }
855
856 // For Field-owned frames, reclaim responsibility in parent's tracker
857 // Only mark as initialized if the child frame was actually initialized.
858 // This prevents double-free when begin_inner/begin_some drops a value via
859 // prepare_for_reinitialization but then fails, leaving the child uninitialized.
860 //
861 // We use require_full_initialization() rather than just is_init because:
862 // - Scalar frames use is_init as the source of truth
863 // - Struct/Array/Enum frames use their iset/data as the source of truth
864 // (is_init may never be set to true for these tracker types)
865 if let FrameOwnership::Field { field_idx } = popped_frame.ownership {
866 // In deferred mode, fill defaults on the child frame before checking initialization.
867 // This handles cases like struct fields inside enum variants inside map values -
868 // the struct's optional fields need defaults filled before we can mark the field
869 // as initialized in the parent enum.
870 if is_deferred {
871 popped_frame.fill_defaults()?;
872 }
873 let child_is_initialized = popped_frame.require_full_initialization().is_ok();
874 match &mut parent_frame.tracker {
875 Tracker::Struct {
876 iset,
877 current_child,
878 } => {
879 if child_is_initialized {
880 iset.set(field_idx); // Parent reclaims responsibility only if child was init
881 }
882 *current_child = None;
883 }
884 Tracker::Array {
885 iset,
886 current_child,
887 } => {
888 if child_is_initialized {
889 iset.set(field_idx); // Parent reclaims responsibility only if child was init
890 }
891 *current_child = None;
892 }
893 Tracker::Enum {
894 data,
895 current_child,
896 ..
897 } => {
898 if child_is_initialized {
899 data.set(field_idx); // Parent reclaims responsibility only if child was init
900 }
901 *current_child = None;
902 }
903 _ => {}
904 }
905 return Ok(self);
906 }
907
908 match &mut parent_frame.tracker {
909 Tracker::SmartPointer => {
910 // We just popped the inner value frame, so now we need to create the smart pointer
911 if let Def::Pointer(smart_ptr_def) = parent_frame.allocated.shape().def {
912 // The inner value must be fully initialized before we can create the smart pointer
913 if let Err(e) = popped_frame.require_full_initialization() {
914 // Inner value wasn't initialized, deallocate and return error
915 popped_frame.deinit();
916 popped_frame.dealloc();
917 return Err(e);
918 }
919
920 let Some(new_into_fn) = smart_ptr_def.vtable.new_into_fn else {
921 popped_frame.deinit();
922 popped_frame.dealloc();
923 return Err(ReflectError::OperationFailed {
924 shape: parent_frame.allocated.shape(),
925 operation: "SmartPointer missing new_into_fn",
926 });
927 };
928
929 // The child frame contained the inner value
930 let inner_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
931
932 // Use new_into_fn to create the Box
933 unsafe {
934 new_into_fn(parent_frame.data, inner_ptr);
935 }
936
937 // We just moved out of it
938 popped_frame.tracker = Tracker::Scalar;
939 popped_frame.is_init = false;
940
941 // Deallocate the inner value's memory since new_into_fn moved it
942 popped_frame.dealloc();
943
944 parent_frame.is_init = true;
945 }
946 }
947 Tracker::List { current_child } if parent_frame.is_init => {
948 if *current_child {
949 // We just popped an element frame, now push it to the list
950 if let Def::List(list_def) = parent_frame.allocated.shape().def {
951 let Some(push_fn) = list_def.push() else {
952 return Err(ReflectError::OperationFailed {
953 shape: parent_frame.allocated.shape(),
954 operation: "List missing push function",
955 });
956 };
957
958 // The child frame contained the element value
959 let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
960
961 // Use push to add element to the list
962 unsafe {
963 push_fn(
964 PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
965 element_ptr,
966 );
967 }
968
969 // Push moved out of popped_frame
970 popped_frame.tracker = Tracker::Scalar;
971 popped_frame.is_init = false;
972 popped_frame.dealloc();
973
974 *current_child = false;
975 }
976 }
977 }
978 Tracker::Map { insert_state } if parent_frame.is_init => {
979 match insert_state {
980 MapInsertState::PushingKey { key_ptr, .. } => {
981 // We just popped the key frame - mark key as initialized and transition
982 // to PushingValue state. key_frame_on_stack = false because the frame
983 // was just popped, so Map now owns the key buffer.
984 *insert_state = MapInsertState::PushingValue {
985 key_ptr: *key_ptr,
986 value_ptr: None,
987 value_initialized: false,
988 value_frame_on_stack: false, // No value frame yet
989 };
990 }
991 MapInsertState::PushingValue {
992 key_ptr, value_ptr, ..
993 } => {
994 // We just popped the value frame, now insert the pair
995 if let (Some(value_ptr), Def::Map(map_def)) =
996 (value_ptr, parent_frame.allocated.shape().def)
997 {
998 let insert = map_def.vtable.insert;
999
1000 // Use insert to add key-value pair to the map
1001 unsafe {
1002 insert(
1003 PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1004 PtrMut::new(key_ptr.as_mut_byte_ptr()),
1005 PtrMut::new(value_ptr.as_mut_byte_ptr()),
1006 );
1007 }
1008
1009 // Note: We don't deallocate the key and value memory here.
1010 // The insert function has semantically moved the values into the map,
1011 // but we still need to deallocate the temporary buffers.
1012 // However, since we don't have frames for them anymore (they were popped),
1013 // we need to handle deallocation here.
1014 if let Ok(key_shape) = map_def.k().layout.sized_layout()
1015 && key_shape.size() > 0
1016 {
1017 unsafe {
1018 ::alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_shape);
1019 }
1020 }
1021 if let Ok(value_shape) = map_def.v().layout.sized_layout()
1022 && value_shape.size() > 0
1023 {
1024 unsafe {
1025 ::alloc::alloc::dealloc(
1026 value_ptr.as_mut_byte_ptr(),
1027 value_shape,
1028 );
1029 }
1030 }
1031
1032 // Reset to idle state
1033 *insert_state = MapInsertState::Idle;
1034 }
1035 }
1036 MapInsertState::Idle => {
1037 // Nothing to do
1038 }
1039 }
1040 }
1041 Tracker::Set { current_child } if parent_frame.is_init => {
1042 if *current_child {
1043 // We just popped an element frame, now insert it into the set
1044 if let Def::Set(set_def) = parent_frame.allocated.shape().def {
1045 let insert = set_def.vtable.insert;
1046
1047 // The child frame contained the element value
1048 let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
1049
1050 // Use insert to add element to the set
1051 unsafe {
1052 insert(
1053 PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1054 element_ptr,
1055 );
1056 }
1057
1058 // Insert moved out of popped_frame
1059 popped_frame.tracker = Tracker::Scalar;
1060 popped_frame.is_init = false;
1061 popped_frame.dealloc();
1062
1063 *current_child = false;
1064 }
1065 }
1066 }
1067 Tracker::Option { building_inner } => {
1068 crate::trace!(
1069 "end(): matched Tracker::Option, building_inner={}",
1070 *building_inner
1071 );
1072 // We just popped the inner value frame for an Option's Some variant
1073 if *building_inner {
1074 if let Def::Option(option_def) = parent_frame.allocated.shape().def {
1075 // Use the Option vtable to initialize Some(inner_value)
1076 let init_some_fn = option_def.vtable.init_some;
1077
1078 // The popped frame contains the inner value
1079 let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
1080
1081 // Initialize the Option as Some(inner_value)
1082 unsafe {
1083 init_some_fn(parent_frame.data, inner_value_ptr);
1084 }
1085
1086 // Deallocate the inner value's memory since init_some_fn moved it
1087 if let FrameOwnership::Owned = popped_frame.ownership
1088 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1089 && layout.size() > 0
1090 {
1091 unsafe {
1092 ::alloc::alloc::dealloc(
1093 popped_frame.data.as_mut_byte_ptr(),
1094 layout,
1095 );
1096 }
1097 }
1098
1099 // Mark that we're no longer building the inner value
1100 *building_inner = false;
1101 crate::trace!("end(): set building_inner to false");
1102 // Mark the Option as initialized
1103 parent_frame.is_init = true;
1104 crate::trace!("end(): set parent_frame.is_init to true");
1105 } else {
1106 return Err(ReflectError::OperationFailed {
1107 shape: parent_frame.allocated.shape(),
1108 operation: "Option frame without Option definition",
1109 });
1110 }
1111 } else {
1112 // building_inner is false - the Option was already initialized but
1113 // begin_some was called again. The popped frame was not used to
1114 // initialize the Option, so we need to clean it up.
1115 popped_frame.deinit();
1116 if let FrameOwnership::Owned = popped_frame.ownership
1117 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1118 && layout.size() > 0
1119 {
1120 unsafe {
1121 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
1122 }
1123 }
1124 }
1125 }
1126 Tracker::Result {
1127 is_ok,
1128 building_inner,
1129 } => {
1130 crate::trace!(
1131 "end(): matched Tracker::Result, is_ok={}, building_inner={}",
1132 *is_ok,
1133 *building_inner
1134 );
1135 // We just popped the inner value frame for a Result's Ok or Err variant
1136 if *building_inner {
1137 if let Def::Result(result_def) = parent_frame.allocated.shape().def {
1138 // The popped frame contains the inner value
1139 let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
1140
1141 // Initialize the Result as Ok(inner_value) or Err(inner_value)
1142 if *is_ok {
1143 let init_ok_fn = result_def.vtable.init_ok;
1144 unsafe {
1145 init_ok_fn(parent_frame.data, inner_value_ptr);
1146 }
1147 } else {
1148 let init_err_fn = result_def.vtable.init_err;
1149 unsafe {
1150 init_err_fn(parent_frame.data, inner_value_ptr);
1151 }
1152 }
1153
1154 // Deallocate the inner value's memory since init_ok/err_fn moved it
1155 if let FrameOwnership::Owned = popped_frame.ownership
1156 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1157 && layout.size() > 0
1158 {
1159 unsafe {
1160 ::alloc::alloc::dealloc(
1161 popped_frame.data.as_mut_byte_ptr(),
1162 layout,
1163 );
1164 }
1165 }
1166
1167 // Mark that we're no longer building the inner value
1168 *building_inner = false;
1169 crate::trace!("end(): set building_inner to false");
1170 // Mark the Result as initialized
1171 parent_frame.is_init = true;
1172 crate::trace!("end(): set parent_frame.is_init to true");
1173 } else {
1174 return Err(ReflectError::OperationFailed {
1175 shape: parent_frame.allocated.shape(),
1176 operation: "Result frame without Result definition",
1177 });
1178 }
1179 } else {
1180 // building_inner is false - the Result was already initialized but
1181 // begin_ok/begin_err was called again. The popped frame was not used to
1182 // initialize the Result, so we need to clean it up.
1183 popped_frame.deinit();
1184 if let FrameOwnership::Owned = popped_frame.ownership
1185 && let Ok(layout) = popped_frame.allocated.shape().layout.sized_layout()
1186 && layout.size() > 0
1187 {
1188 unsafe {
1189 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
1190 }
1191 }
1192 }
1193 }
1194 Tracker::Scalar => {
1195 // the main case here is: the popped frame was a `String` and the
1196 // parent frame is an `Arc<str>`, `Box<str>` etc.
1197 match &parent_frame.allocated.shape().def {
1198 Def::Pointer(smart_ptr_def) => {
1199 let pointee =
1200 smart_ptr_def
1201 .pointee()
1202 .ok_or(ReflectError::InvariantViolation {
1203 invariant: "pointer type doesn't have a pointee",
1204 })?;
1205
1206 if !pointee.is_shape(str::SHAPE) {
1207 return Err(ReflectError::InvariantViolation {
1208 invariant: "only T=str is supported when building SmartPointer<T> and T is unsized",
1209 });
1210 }
1211
1212 if !popped_frame.allocated.shape().is_shape(String::SHAPE) {
1213 return Err(ReflectError::InvariantViolation {
1214 invariant: "the popped frame should be String when building a SmartPointer<T>",
1215 });
1216 }
1217
1218 popped_frame.require_full_initialization()?;
1219
1220 // if the just-popped frame was a SmartPointerStr, we have some conversion to do:
1221 // Special-case: SmartPointer<str> (Box<str>, Arc<str>, Rc<str>) via SmartPointerStr tracker
1222 // Here, popped_frame actually contains a value for String that should be moved into the smart pointer.
1223 // We convert the String into Box<str>, Arc<str>, or Rc<str> as appropriate and write it to the parent frame.
1224 use ::alloc::{rc::Rc, string::String, sync::Arc};
1225 let parent_shape = parent_frame.allocated.shape();
1226
1227 let Some(known) = smart_ptr_def.known else {
1228 return Err(ReflectError::OperationFailed {
1229 shape: parent_shape,
1230 operation: "SmartPointerStr for unknown smart pointer kind",
1231 });
1232 };
1233
1234 parent_frame.deinit();
1235
1236 // Interpret the memory as a String, then convert and write.
1237 let string_ptr = popped_frame.data.as_mut_byte_ptr() as *mut String;
1238 let string_value = unsafe { core::ptr::read(string_ptr) };
1239
1240 match known {
1241 KnownPointer::Box => {
1242 let boxed: Box<str> = string_value.into_boxed_str();
1243 unsafe {
1244 core::ptr::write(
1245 parent_frame.data.as_mut_byte_ptr() as *mut Box<str>,
1246 boxed,
1247 );
1248 }
1249 }
1250 KnownPointer::Arc => {
1251 let arc: Arc<str> = Arc::from(string_value.into_boxed_str());
1252 unsafe {
1253 core::ptr::write(
1254 parent_frame.data.as_mut_byte_ptr() as *mut Arc<str>,
1255 arc,
1256 );
1257 }
1258 }
1259 KnownPointer::Rc => {
1260 let rc: Rc<str> = Rc::from(string_value.into_boxed_str());
1261 unsafe {
1262 core::ptr::write(
1263 parent_frame.data.as_mut_byte_ptr() as *mut Rc<str>,
1264 rc,
1265 );
1266 }
1267 }
1268 _ => {
1269 return Err(ReflectError::OperationFailed {
1270 shape: parent_shape,
1271 operation: "Don't know how to build this pointer type",
1272 });
1273 }
1274 }
1275
1276 parent_frame.is_init = true;
1277
1278 popped_frame.tracker = Tracker::Scalar;
1279 popped_frame.is_init = false;
1280 popped_frame.dealloc();
1281 }
1282 _ => {
1283 // This can happen if begin_inner() was called on a type that
1284 // has shape.inner but isn't a SmartPointer (e.g., Option).
1285 // In this case, we can't complete the conversion, so return error.
1286 return Err(ReflectError::OperationFailed {
1287 shape: parent_frame.allocated.shape(),
1288 operation: "end() called but parent has Uninit/Init tracker and isn't a SmartPointer",
1289 });
1290 }
1291 }
1292 }
1293 Tracker::SmartPointerSlice {
1294 vtable,
1295 building_item,
1296 } => {
1297 if *building_item {
1298 // We just popped an element frame, now push it to the slice builder
1299 let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
1300
1301 // Use the slice builder's push_fn to add the element
1302 crate::trace!("Pushing element to slice builder");
1303 unsafe {
1304 let parent_ptr = parent_frame.data.assume_init();
1305 (vtable.push_fn)(parent_ptr, element_ptr);
1306 }
1307
1308 popped_frame.tracker = Tracker::Scalar;
1309 popped_frame.is_init = false;
1310 popped_frame.dealloc();
1311
1312 if let Tracker::SmartPointerSlice {
1313 building_item: bi, ..
1314 } = &mut parent_frame.tracker
1315 {
1316 *bi = false;
1317 }
1318 }
1319 }
1320 Tracker::DynamicValue {
1321 state: DynamicValueState::Array { building_element },
1322 } => {
1323 if *building_element {
1324 // Check that the element is initialized before pushing
1325 if !popped_frame.is_init {
1326 // Element was never set - clean up and return error
1327 let shape = parent_frame.allocated.shape();
1328 popped_frame.dealloc();
1329 *building_element = false;
1330 // No need to poison - returning Err consumes self, Drop will handle cleanup
1331 return Err(ReflectError::OperationFailed {
1332 shape,
1333 operation: "end() called but array element was never initialized",
1334 });
1335 }
1336
1337 // We just popped an element frame, now push it to the dynamic array
1338 if let Def::DynamicValue(dyn_def) = parent_frame.allocated.shape().def {
1339 // Get mutable pointers - both array and element need PtrMut
1340 let array_ptr = unsafe { parent_frame.data.assume_init() };
1341 let element_ptr = unsafe { popped_frame.data.assume_init() };
1342
1343 // Use push_array_element to add element to the array
1344 unsafe {
1345 (dyn_def.vtable.push_array_element)(array_ptr, element_ptr);
1346 }
1347
1348 // Push moved out of popped_frame
1349 popped_frame.tracker = Tracker::Scalar;
1350 popped_frame.is_init = false;
1351 popped_frame.dealloc();
1352
1353 *building_element = false;
1354 }
1355 }
1356 }
1357 Tracker::DynamicValue {
1358 state: DynamicValueState::Object { insert_state },
1359 } => {
1360 if let DynamicObjectInsertState::BuildingValue { key } = insert_state {
1361 // Check that the value is initialized before inserting
1362 if !popped_frame.is_init {
1363 // Value was never set - clean up and return error
1364 let shape = parent_frame.allocated.shape();
1365 popped_frame.dealloc();
1366 *insert_state = DynamicObjectInsertState::Idle;
1367 // No need to poison - returning Err consumes self, Drop will handle cleanup
1368 return Err(ReflectError::OperationFailed {
1369 shape,
1370 operation: "end() called but object entry value was never initialized",
1371 });
1372 }
1373
1374 // We just popped a value frame, now insert it into the dynamic object
1375 if let Def::DynamicValue(dyn_def) = parent_frame.allocated.shape().def {
1376 // Get mutable pointers - both object and value need PtrMut
1377 let object_ptr = unsafe { parent_frame.data.assume_init() };
1378 let value_ptr = unsafe { popped_frame.data.assume_init() };
1379
1380 // Use insert_object_entry to add the key-value pair
1381 unsafe {
1382 (dyn_def.vtable.insert_object_entry)(object_ptr, key, value_ptr);
1383 }
1384
1385 // Insert moved out of popped_frame
1386 popped_frame.tracker = Tracker::Scalar;
1387 popped_frame.is_init = false;
1388 popped_frame.dealloc();
1389
1390 // Reset insert state to Idle
1391 *insert_state = DynamicObjectInsertState::Idle;
1392 }
1393 }
1394 }
1395 _ => {}
1396 }
1397
1398 Ok(self)
1399 }
1400
1401 /// Returns a human-readable path representing the current traversal in the builder,
1402 /// e.g., `RootStruct.fieldName[index].subfield`.
1403 pub fn path(&self) -> String {
1404 let mut out = String::new();
1405
1406 let mut path_components = Vec::new();
1407 // The stack of enum/struct/sequence names currently in context.
1408 // Start from root and build upwards.
1409 for (i, frame) in self.frames().iter().enumerate() {
1410 match frame.allocated.shape().ty {
1411 Type::User(user_type) => match user_type {
1412 UserType::Struct(struct_type) => {
1413 // Try to get currently active field index
1414 let mut field_str = None;
1415 if let Tracker::Struct {
1416 current_child: Some(idx),
1417 ..
1418 } = &frame.tracker
1419 && let Some(field) = struct_type.fields.get(*idx)
1420 {
1421 field_str = Some(field.name);
1422 }
1423 if i == 0 {
1424 // Use Display for the root struct shape
1425 path_components.push(format!("{}", frame.allocated.shape()));
1426 }
1427 if let Some(field_name) = field_str {
1428 path_components.push(format!(".{field_name}"));
1429 }
1430 }
1431 UserType::Enum(_enum_type) => {
1432 // Try to get currently active variant and field
1433 if let Tracker::Enum {
1434 variant,
1435 current_child,
1436 ..
1437 } = &frame.tracker
1438 {
1439 if i == 0 {
1440 // Use Display for the root enum shape
1441 path_components.push(format!("{}", frame.allocated.shape()));
1442 }
1443 path_components.push(format!("::{}", variant.name));
1444 if let Some(idx) = *current_child
1445 && let Some(field) = variant.data.fields.get(idx)
1446 {
1447 path_components.push(format!(".{}", field.name));
1448 }
1449 } else if i == 0 {
1450 // just the enum display
1451 path_components.push(format!("{}", frame.allocated.shape()));
1452 }
1453 }
1454 UserType::Union(_union_type) => {
1455 path_components.push(format!("{}", frame.allocated.shape()));
1456 }
1457 UserType::Opaque => {
1458 path_components.push("<opaque>".to_string());
1459 }
1460 },
1461 Type::Sequence(seq_type) => match seq_type {
1462 facet_core::SequenceType::Array(_array_def) => {
1463 // Try to show current element index
1464 if let Tracker::Array {
1465 current_child: Some(idx),
1466 ..
1467 } = &frame.tracker
1468 {
1469 path_components.push(format!("[{idx}]"));
1470 }
1471 }
1472 // You can add more for Slice, Vec, etc., if applicable
1473 _ => {
1474 // just indicate "[]" for sequence
1475 path_components.push("[]".to_string());
1476 }
1477 },
1478 Type::Pointer(_) => {
1479 // Indicate deref
1480 path_components.push("*".to_string());
1481 }
1482 _ => {
1483 // No structural path
1484 }
1485 }
1486 }
1487 // Merge the path_components into a single string
1488 for component in path_components {
1489 out.push_str(&component);
1490 }
1491 out
1492 }
1493
1494 /// Get the field for the parent frame
1495 pub fn parent_field(&self) -> Option<&Field> {
1496 self.frames()
1497 .iter()
1498 .rev()
1499 .nth(1)
1500 .and_then(|f| f.get_field())
1501 }
1502
1503 /// Gets the field for the current frame
1504 pub fn current_field(&self) -> Option<&Field> {
1505 self.frames().last().and_then(|f| f.get_field())
1506 }
1507
1508 /// Returns a const pointer to the current frame's data.
1509 ///
1510 /// This is useful for validation - after deserializing a field value,
1511 /// validators can read the value through this pointer.
1512 ///
1513 /// # Safety
1514 ///
1515 /// The returned pointer is valid only while the frame exists.
1516 /// The caller must ensure the frame is fully initialized before
1517 /// reading through this pointer.
1518 pub fn data_ptr(&self) -> Option<facet_core::PtrConst> {
1519 if self.state != PartialState::Active {
1520 return None;
1521 }
1522 self.frames().last().map(|f| {
1523 // SAFETY: We're in active state, so the frame is valid.
1524 // The caller is responsible for ensuring the data is initialized.
1525 unsafe { f.data.assume_init().as_const() }
1526 })
1527 }
1528}