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