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