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 // Fill defaults before checking full initialization
592 // This is important for structs in deferred mode that are children of collections
593 // (lists, maps, etc.) - they must be fully initialized before being inserted,
594 // but defaults should be applied first.
595 if self.is_deferred()
596 && let Some(frame) = self.frames_mut().last_mut()
597 {
598 crate::trace!(
599 "end(): Filling defaults before full init check for {}, tracker={:?}",
600 frame.shape,
601 frame.tracker.kind()
602 );
603 frame.fill_defaults()?;
604 }
605
606 let frame = self.frames().last().unwrap();
607 crate::trace!(
608 "end(): Checking full init for {}, tracker={:?}, is_init={}",
609 frame.shape,
610 frame.tracker.kind(),
611 frame.is_init
612 );
613 let result = frame.require_full_initialization();
614 crate::trace!(
615 "end(): require_full_initialization result: {:?}",
616 result.is_ok()
617 );
618 result?
619 }
620
621 // Pop the frame and save its data pointer for SmartPointer handling
622 let mut popped_frame = self.frames_mut().pop().unwrap();
623
624 // In deferred mode, store the frame for potential re-entry and skip
625 // the normal parent-updating logic. The frame will be finalized later
626 // in finish_deferred().
627 //
628 // We only store if the path depth matches the frame depth, meaning we're
629 // ending a tracked struct/enum field, not something like begin_some()
630 // or a field inside a collection item.
631 if let FrameMode::Deferred {
632 stack,
633 start_depth,
634 current_path,
635 stored_frames,
636 ..
637 } = &mut self.mode
638 {
639 // Path depth should match the relative frame depth for a tracked field.
640 // After popping: frames.len() - start_depth + 1 should equal path.len()
641 // for fields entered via begin_field (not begin_some/begin_inner).
642 let relative_depth = stack.len() - *start_depth + 1;
643 let is_tracked_field = !current_path.is_empty() && current_path.len() == relative_depth;
644
645 if is_tracked_field {
646 trace!(
647 "end(): Storing frame for deferred path {:?}, shape {}",
648 current_path, popped_frame.shape
649 );
650
651 // Store the frame at the current path
652 let path = current_path.clone();
653 stored_frames.insert(path, popped_frame);
654
655 // Pop from current_path
656 current_path.pop();
657
658 // Clear parent's current_child tracking
659 if let Some(parent_frame) = stack.last_mut() {
660 parent_frame.tracker.clear_current_child();
661 }
662
663 return Ok(self);
664 }
665 }
666
667 // check if this needs deserialization from a different shape
668 if popped_frame.using_custom_deserialization {
669 // Check for field-level proxy first, then fall back to shape-level proxy
670 let deserialize_with: Option<facet_core::ProxyConvertInFn> = self
671 .parent_field()
672 .and_then(|f| f.proxy().map(|p| p.convert_in));
673
674 // Fall back to shape-level proxy stored in the frame
675 let deserialize_with =
676 deserialize_with.or_else(|| popped_frame.shape_level_proxy.map(|p| p.convert_in));
677
678 if let Some(deserialize_with) = deserialize_with {
679 let parent_frame = self.frames_mut().last_mut().unwrap();
680
681 trace!(
682 "Detected custom conversion needed from {} to {}",
683 popped_frame.shape, parent_frame.shape
684 );
685
686 unsafe {
687 let res = {
688 let inner_value_ptr = popped_frame.data.assume_init().as_const();
689 (deserialize_with)(inner_value_ptr, parent_frame.data)
690 };
691 let popped_frame_shape = popped_frame.shape;
692
693 // Note: We do NOT call deinit() here because deserialize_with uses
694 // ptr::read to take ownership of the source value. Calling deinit()
695 // would cause a double-free. We mark is_init as false to satisfy
696 // dealloc()'s assertion, then deallocate the memory.
697 popped_frame.is_init = false;
698 popped_frame.dealloc();
699 let rptr = res.map_err(|message| ReflectError::CustomDeserializationError {
700 message,
701 src_shape: popped_frame_shape,
702 dst_shape: parent_frame.shape,
703 })?;
704 if rptr.as_uninit() != parent_frame.data {
705 return Err(ReflectError::CustomDeserializationError {
706 message: "deserialize_with did not return the expected pointer".into(),
707 src_shape: popped_frame_shape,
708 dst_shape: parent_frame.shape,
709 });
710 }
711 parent_frame.mark_as_init();
712 }
713 return Ok(self);
714 }
715 }
716
717 // Update parent frame's tracking when popping from a child
718 let parent_frame = self.frames_mut().last_mut().unwrap();
719
720 crate::trace!(
721 "end(): Popped {} (tracker {:?}), Parent {} (tracker {:?})",
722 popped_frame.shape,
723 popped_frame.tracker.kind(),
724 parent_frame.shape,
725 parent_frame.tracker.kind()
726 );
727
728 // Check if we need to do a conversion - this happens when:
729 // 1. The parent frame has a builder_shape or inner type that matches the popped frame's shape
730 // 2. The parent frame has try_from
731 // 3. The parent frame is not yet initialized
732 // 4. The parent frame's tracker is Scalar (not Option, SmartPointer, etc.)
733 // This ensures we only do conversion when begin_inner was used, not begin_some
734 let needs_conversion = !parent_frame.is_init
735 && matches!(parent_frame.tracker, Tracker::Scalar)
736 && ((parent_frame.shape.builder_shape.is_some()
737 && parent_frame.shape.builder_shape.unwrap() == popped_frame.shape)
738 || (parent_frame.shape.inner.is_some()
739 && parent_frame.shape.inner.unwrap() == popped_frame.shape))
740 && match parent_frame.shape.vtable {
741 facet_core::VTableErased::Direct(vt) => vt.try_from.is_some(),
742 facet_core::VTableErased::Indirect(vt) => vt.try_from.is_some(),
743 };
744
745 if needs_conversion {
746 trace!(
747 "Detected implicit conversion needed from {} to {}",
748 popped_frame.shape, parent_frame.shape
749 );
750
751 // The conversion requires the source frame to be fully initialized
752 // (we're about to call assume_init() and pass to try_from)
753 if let Err(e) = popped_frame.require_full_initialization() {
754 // Deallocate the memory since the frame wasn't fully initialized
755 if let FrameOwnership::Owned = popped_frame.ownership
756 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
757 && layout.size() > 0
758 {
759 trace!(
760 "Deallocating uninitialized conversion frame memory: size={}, align={}",
761 layout.size(),
762 layout.align()
763 );
764 unsafe {
765 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
766 }
767 }
768 return Err(e);
769 }
770
771 // Perform the conversion
772 let inner_ptr = unsafe { popped_frame.data.assume_init().as_const() };
773 let inner_shape = popped_frame.shape;
774
775 trace!("Converting from {} to {}", inner_shape, parent_frame.shape);
776
777 // Handle Direct and Indirect vtables differently due to different return types
778 let result = match parent_frame.shape.vtable {
779 facet_core::VTableErased::Direct(vt) => {
780 if let Some(try_from_fn) = vt.try_from {
781 unsafe {
782 try_from_fn(
783 parent_frame.data.as_mut_byte_ptr() as *mut (),
784 inner_shape,
785 inner_ptr,
786 )
787 }
788 } else {
789 return Err(ReflectError::OperationFailed {
790 shape: parent_frame.shape,
791 operation: "try_from not available for this type",
792 });
793 }
794 }
795 facet_core::VTableErased::Indirect(vt) => {
796 if let Some(try_from_fn) = vt.try_from {
797 let ox_mut = facet_core::OxMut::new(
798 unsafe { parent_frame.data.assume_init() },
799 parent_frame.shape,
800 );
801 match unsafe { try_from_fn(ox_mut.into(), inner_shape, inner_ptr) } {
802 Some(result) => result,
803 None => {
804 return Err(ReflectError::OperationFailed {
805 shape: parent_frame.shape,
806 operation: "try_from not available for inner type",
807 });
808 }
809 }
810 } else {
811 return Err(ReflectError::OperationFailed {
812 shape: parent_frame.shape,
813 operation: "try_from not available for this type",
814 });
815 }
816 }
817 };
818
819 if let Err(e) = result {
820 trace!("Conversion failed: {e:?}");
821
822 // Deallocate the inner value's memory since conversion failed
823 if let FrameOwnership::Owned = popped_frame.ownership
824 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
825 && layout.size() > 0
826 {
827 trace!(
828 "Deallocating conversion frame memory after failure: size={}, align={}",
829 layout.size(),
830 layout.align()
831 );
832 unsafe {
833 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
834 }
835 }
836
837 return Err(ReflectError::TryFromError {
838 src_shape: inner_shape,
839 dst_shape: parent_frame.shape,
840 inner: facet_core::TryFromError::Generic(e),
841 });
842 }
843
844 trace!("Conversion succeeded, marking parent as initialized");
845 parent_frame.is_init = true;
846
847 // Deallocate the inner value's memory since try_from consumed it
848 if let FrameOwnership::Owned = popped_frame.ownership
849 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
850 && layout.size() > 0
851 {
852 trace!(
853 "Deallocating conversion frame memory: size={}, align={}",
854 layout.size(),
855 layout.align()
856 );
857 unsafe {
858 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
859 }
860 }
861
862 return Ok(self);
863 }
864
865 // For Field-owned frames, reclaim responsibility in parent's tracker
866 // Only mark as initialized if the child frame was actually initialized.
867 // This prevents double-free when begin_inner/begin_some drops a value via
868 // prepare_for_reinitialization but then fails, leaving the child uninitialized.
869 //
870 // We use require_full_initialization() rather than just is_init because:
871 // - Scalar frames use is_init as the source of truth
872 // - Struct/Array/Enum frames use their iset/data as the source of truth
873 // (is_init may never be set to true for these tracker types)
874 if let FrameOwnership::Field { field_idx } = popped_frame.ownership {
875 let child_is_initialized = popped_frame.require_full_initialization().is_ok();
876 match &mut parent_frame.tracker {
877 Tracker::Struct {
878 iset,
879 current_child,
880 } => {
881 if child_is_initialized {
882 iset.set(field_idx); // Parent reclaims responsibility only if child was init
883 }
884 *current_child = None;
885 }
886 Tracker::Array {
887 iset,
888 current_child,
889 } => {
890 if child_is_initialized {
891 iset.set(field_idx); // Parent reclaims responsibility only if child was init
892 }
893 *current_child = None;
894 }
895 Tracker::Enum {
896 data,
897 current_child,
898 ..
899 } => {
900 if child_is_initialized {
901 data.set(field_idx); // Parent reclaims responsibility only if child was init
902 }
903 *current_child = None;
904 }
905 _ => {}
906 }
907 return Ok(self);
908 }
909
910 match &mut parent_frame.tracker {
911 Tracker::SmartPointer => {
912 // We just popped the inner value frame, so now we need to create the smart pointer
913 if let Def::Pointer(smart_ptr_def) = parent_frame.shape.def {
914 // The inner value must be fully initialized before we can create the smart pointer
915 if let Err(e) = popped_frame.require_full_initialization() {
916 // Inner value wasn't initialized, deallocate and return error
917 popped_frame.deinit();
918 popped_frame.dealloc();
919 return Err(e);
920 }
921
922 let Some(new_into_fn) = smart_ptr_def.vtable.new_into_fn else {
923 popped_frame.deinit();
924 popped_frame.dealloc();
925 return Err(ReflectError::OperationFailed {
926 shape: parent_frame.shape,
927 operation: "SmartPointer missing new_into_fn",
928 });
929 };
930
931 // The child frame contained the inner value
932 let inner_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
933
934 // Use new_into_fn to create the Box
935 unsafe {
936 new_into_fn(parent_frame.data, inner_ptr);
937 }
938
939 // We just moved out of it
940 popped_frame.tracker = Tracker::Scalar;
941 popped_frame.is_init = false;
942
943 // Deallocate the inner value's memory since new_into_fn moved it
944 popped_frame.dealloc();
945
946 parent_frame.is_init = true;
947 }
948 }
949 Tracker::List { current_child } if parent_frame.is_init => {
950 if *current_child {
951 // We just popped an element frame, now push it to the list
952 if let Def::List(list_def) = parent_frame.shape.def {
953 let Some(push_fn) = list_def.push() else {
954 return Err(ReflectError::OperationFailed {
955 shape: parent_frame.shape,
956 operation: "List missing push function",
957 });
958 };
959
960 // The child frame contained the element value
961 let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
962
963 // Use push to add element to the list
964 unsafe {
965 push_fn(
966 PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
967 element_ptr,
968 );
969 }
970
971 // Push moved out of popped_frame
972 popped_frame.tracker = Tracker::Scalar;
973 popped_frame.is_init = false;
974 popped_frame.dealloc();
975
976 *current_child = false;
977 }
978 }
979 }
980 Tracker::Map { insert_state } if parent_frame.is_init => {
981 match insert_state {
982 MapInsertState::PushingKey { key_ptr, .. } => {
983 // We just popped the key frame - mark key as initialized and transition
984 // to PushingValue state
985 *insert_state = MapInsertState::PushingValue {
986 key_ptr: *key_ptr,
987 value_ptr: None,
988 value_initialized: false,
989 };
990 }
991 MapInsertState::PushingValue {
992 key_ptr, value_ptr, ..
993 } => {
994 // We just popped the value frame, now insert the pair
995 if let (Some(value_ptr), Def::Map(map_def)) =
996 (value_ptr, parent_frame.shape.def)
997 {
998 let insert = map_def.vtable.insert;
999
1000 // Use insert to add key-value pair to the map
1001 unsafe {
1002 insert(
1003 PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1004 PtrMut::new(key_ptr.as_mut_byte_ptr()),
1005 PtrMut::new(value_ptr.as_mut_byte_ptr()),
1006 );
1007 }
1008
1009 // Note: We don't deallocate the key and value memory here.
1010 // The insert function has semantically moved the values into the map,
1011 // but we still need to deallocate the temporary buffers.
1012 // However, since we don't have frames for them anymore (they were popped),
1013 // we need to handle deallocation here.
1014 if let Ok(key_shape) = map_def.k().layout.sized_layout()
1015 && key_shape.size() > 0
1016 {
1017 unsafe {
1018 ::alloc::alloc::dealloc(key_ptr.as_mut_byte_ptr(), key_shape);
1019 }
1020 }
1021 if let Ok(value_shape) = map_def.v().layout.sized_layout()
1022 && value_shape.size() > 0
1023 {
1024 unsafe {
1025 ::alloc::alloc::dealloc(
1026 value_ptr.as_mut_byte_ptr(),
1027 value_shape,
1028 );
1029 }
1030 }
1031
1032 // Reset to idle state
1033 *insert_state = MapInsertState::Idle;
1034 }
1035 }
1036 MapInsertState::Idle => {
1037 // Nothing to do
1038 }
1039 }
1040 }
1041 Tracker::Set { current_child } if parent_frame.is_init => {
1042 if *current_child {
1043 // We just popped an element frame, now insert it into the set
1044 if let Def::Set(set_def) = parent_frame.shape.def {
1045 let insert = set_def.vtable.insert;
1046
1047 // The child frame contained the element value
1048 let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
1049
1050 // Use insert to add element to the set
1051 unsafe {
1052 insert(
1053 PtrMut::new(parent_frame.data.as_mut_byte_ptr()),
1054 element_ptr,
1055 );
1056 }
1057
1058 // Insert moved out of popped_frame
1059 popped_frame.tracker = Tracker::Scalar;
1060 popped_frame.is_init = false;
1061 popped_frame.dealloc();
1062
1063 *current_child = false;
1064 }
1065 }
1066 }
1067 Tracker::Option { building_inner } => {
1068 crate::trace!(
1069 "end(): matched Tracker::Option, building_inner={}",
1070 *building_inner
1071 );
1072 // We just popped the inner value frame for an Option's Some variant
1073 if *building_inner {
1074 if let Def::Option(option_def) = parent_frame.shape.def {
1075 // Use the Option vtable to initialize Some(inner_value)
1076 let init_some_fn = option_def.vtable.init_some;
1077
1078 // The popped frame contains the inner value
1079 let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
1080
1081 // Initialize the Option as Some(inner_value)
1082 unsafe {
1083 init_some_fn(parent_frame.data, inner_value_ptr);
1084 }
1085
1086 // Deallocate the inner value's memory since init_some_fn moved it
1087 if let FrameOwnership::Owned = popped_frame.ownership
1088 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
1089 && layout.size() > 0
1090 {
1091 unsafe {
1092 ::alloc::alloc::dealloc(
1093 popped_frame.data.as_mut_byte_ptr(),
1094 layout,
1095 );
1096 }
1097 }
1098
1099 // Mark that we're no longer building the inner value
1100 *building_inner = false;
1101 crate::trace!("end(): set building_inner to false");
1102 // Mark the Option as initialized
1103 parent_frame.is_init = true;
1104 crate::trace!("end(): set parent_frame.is_init to true");
1105 } else {
1106 return Err(ReflectError::OperationFailed {
1107 shape: parent_frame.shape,
1108 operation: "Option frame without Option definition",
1109 });
1110 }
1111 } else {
1112 // building_inner is false - the Option was already initialized but
1113 // begin_some was called again. The popped frame was not used to
1114 // initialize the Option, so we need to clean it up.
1115 popped_frame.deinit();
1116 if let FrameOwnership::Owned = popped_frame.ownership
1117 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
1118 && layout.size() > 0
1119 {
1120 unsafe {
1121 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
1122 }
1123 }
1124 }
1125 }
1126 Tracker::Result {
1127 is_ok,
1128 building_inner,
1129 } => {
1130 crate::trace!(
1131 "end(): matched Tracker::Result, is_ok={}, building_inner={}",
1132 *is_ok,
1133 *building_inner
1134 );
1135 // We just popped the inner value frame for a Result's Ok or Err variant
1136 if *building_inner {
1137 if let Def::Result(result_def) = parent_frame.shape.def {
1138 // The popped frame contains the inner value
1139 let inner_value_ptr = unsafe { popped_frame.data.assume_init().as_const() };
1140
1141 // Initialize the Result as Ok(inner_value) or Err(inner_value)
1142 if *is_ok {
1143 let init_ok_fn = result_def.vtable.init_ok;
1144 unsafe {
1145 init_ok_fn(parent_frame.data, inner_value_ptr);
1146 }
1147 } else {
1148 let init_err_fn = result_def.vtable.init_err;
1149 unsafe {
1150 init_err_fn(parent_frame.data, inner_value_ptr);
1151 }
1152 }
1153
1154 // Deallocate the inner value's memory since init_ok/err_fn moved it
1155 if let FrameOwnership::Owned = popped_frame.ownership
1156 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
1157 && layout.size() > 0
1158 {
1159 unsafe {
1160 ::alloc::alloc::dealloc(
1161 popped_frame.data.as_mut_byte_ptr(),
1162 layout,
1163 );
1164 }
1165 }
1166
1167 // Mark that we're no longer building the inner value
1168 *building_inner = false;
1169 crate::trace!("end(): set building_inner to false");
1170 // Mark the Result as initialized
1171 parent_frame.is_init = true;
1172 crate::trace!("end(): set parent_frame.is_init to true");
1173 } else {
1174 return Err(ReflectError::OperationFailed {
1175 shape: parent_frame.shape,
1176 operation: "Result frame without Result definition",
1177 });
1178 }
1179 } else {
1180 // building_inner is false - the Result was already initialized but
1181 // begin_ok/begin_err was called again. The popped frame was not used to
1182 // initialize the Result, so we need to clean it up.
1183 popped_frame.deinit();
1184 if let FrameOwnership::Owned = popped_frame.ownership
1185 && let Ok(layout) = popped_frame.shape.layout.sized_layout()
1186 && layout.size() > 0
1187 {
1188 unsafe {
1189 ::alloc::alloc::dealloc(popped_frame.data.as_mut_byte_ptr(), layout);
1190 }
1191 }
1192 }
1193 }
1194 Tracker::Scalar => {
1195 // the main case here is: the popped frame was a `String` and the
1196 // parent frame is an `Arc<str>`, `Box<str>` etc.
1197 match &parent_frame.shape.def {
1198 Def::Pointer(smart_ptr_def) => {
1199 let pointee =
1200 smart_ptr_def
1201 .pointee()
1202 .ok_or(ReflectError::InvariantViolation {
1203 invariant: "pointer type doesn't have a pointee",
1204 })?;
1205
1206 if !pointee.is_shape(str::SHAPE) {
1207 return Err(ReflectError::InvariantViolation {
1208 invariant: "only T=str is supported when building SmartPointer<T> and T is unsized",
1209 });
1210 }
1211
1212 if !popped_frame.shape.is_shape(String::SHAPE) {
1213 return Err(ReflectError::InvariantViolation {
1214 invariant: "the popped frame should be String when building a SmartPointer<T>",
1215 });
1216 }
1217
1218 popped_frame.require_full_initialization()?;
1219
1220 // if the just-popped frame was a SmartPointerStr, we have some conversion to do:
1221 // Special-case: SmartPointer<str> (Box<str>, Arc<str>, Rc<str>) via SmartPointerStr tracker
1222 // Here, popped_frame actually contains a value for String that should be moved into the smart pointer.
1223 // We convert the String into Box<str>, Arc<str>, or Rc<str> as appropriate and write it to the parent frame.
1224 use ::alloc::{rc::Rc, string::String, sync::Arc};
1225 let parent_shape = parent_frame.shape;
1226
1227 let Some(known) = smart_ptr_def.known else {
1228 return Err(ReflectError::OperationFailed {
1229 shape: parent_shape,
1230 operation: "SmartPointerStr for unknown smart pointer kind",
1231 });
1232 };
1233
1234 parent_frame.deinit();
1235
1236 // Interpret the memory as a String, then convert and write.
1237 let string_ptr = popped_frame.data.as_mut_byte_ptr() as *mut String;
1238 let string_value = unsafe { core::ptr::read(string_ptr) };
1239
1240 match known {
1241 KnownPointer::Box => {
1242 let boxed: Box<str> = string_value.into_boxed_str();
1243 unsafe {
1244 core::ptr::write(
1245 parent_frame.data.as_mut_byte_ptr() as *mut Box<str>,
1246 boxed,
1247 );
1248 }
1249 }
1250 KnownPointer::Arc => {
1251 let arc: Arc<str> = Arc::from(string_value.into_boxed_str());
1252 unsafe {
1253 core::ptr::write(
1254 parent_frame.data.as_mut_byte_ptr() as *mut Arc<str>,
1255 arc,
1256 );
1257 }
1258 }
1259 KnownPointer::Rc => {
1260 let rc: Rc<str> = Rc::from(string_value.into_boxed_str());
1261 unsafe {
1262 core::ptr::write(
1263 parent_frame.data.as_mut_byte_ptr() as *mut Rc<str>,
1264 rc,
1265 );
1266 }
1267 }
1268 _ => {
1269 return Err(ReflectError::OperationFailed {
1270 shape: parent_shape,
1271 operation: "Don't know how to build this pointer type",
1272 });
1273 }
1274 }
1275
1276 parent_frame.is_init = true;
1277
1278 popped_frame.tracker = Tracker::Scalar;
1279 popped_frame.is_init = false;
1280 popped_frame.dealloc();
1281 }
1282 _ => {
1283 // This can happen if begin_inner() was called on a type that
1284 // has shape.inner but isn't a SmartPointer (e.g., Option).
1285 // In this case, we can't complete the conversion, so return error.
1286 return Err(ReflectError::OperationFailed {
1287 shape: parent_frame.shape,
1288 operation: "end() called but parent has Uninit/Init tracker and isn't a SmartPointer",
1289 });
1290 }
1291 }
1292 }
1293 Tracker::SmartPointerSlice {
1294 vtable,
1295 building_item,
1296 } => {
1297 if *building_item {
1298 // We just popped an element frame, now push it to the slice builder
1299 let element_ptr = PtrMut::new(popped_frame.data.as_mut_byte_ptr());
1300
1301 // Use the slice builder's push_fn to add the element
1302 crate::trace!("Pushing element to slice builder");
1303 unsafe {
1304 let parent_ptr = parent_frame.data.assume_init();
1305 (vtable.push_fn)(parent_ptr, element_ptr);
1306 }
1307
1308 popped_frame.tracker = Tracker::Scalar;
1309 popped_frame.is_init = false;
1310 popped_frame.dealloc();
1311
1312 if let Tracker::SmartPointerSlice {
1313 building_item: bi, ..
1314 } = &mut parent_frame.tracker
1315 {
1316 *bi = false;
1317 }
1318 }
1319 }
1320 Tracker::DynamicValue {
1321 state: DynamicValueState::Array { building_element },
1322 } => {
1323 if *building_element {
1324 // Check that the element is initialized before pushing
1325 if !popped_frame.is_init {
1326 // Element was never set - clean up and return error
1327 let shape = parent_frame.shape;
1328 popped_frame.dealloc();
1329 *building_element = false;
1330 // No need to poison - returning Err consumes self, Drop will handle cleanup
1331 return Err(ReflectError::OperationFailed {
1332 shape,
1333 operation: "end() called but array element was never initialized",
1334 });
1335 }
1336
1337 // We just popped an element frame, now push it to the dynamic array
1338 if let Def::DynamicValue(dyn_def) = parent_frame.shape.def {
1339 // Get mutable pointers - both array and element need PtrMut
1340 let array_ptr = unsafe { parent_frame.data.assume_init() };
1341 let element_ptr = unsafe { popped_frame.data.assume_init() };
1342
1343 // Use push_array_element to add element to the array
1344 unsafe {
1345 (dyn_def.vtable.push_array_element)(array_ptr, element_ptr);
1346 }
1347
1348 // Push moved out of popped_frame
1349 popped_frame.tracker = Tracker::Scalar;
1350 popped_frame.is_init = false;
1351 popped_frame.dealloc();
1352
1353 *building_element = false;
1354 }
1355 }
1356 }
1357 Tracker::DynamicValue {
1358 state: DynamicValueState::Object { insert_state },
1359 } => {
1360 if let DynamicObjectInsertState::BuildingValue { key } = insert_state {
1361 // Check that the value is initialized before inserting
1362 if !popped_frame.is_init {
1363 // Value was never set - clean up and return error
1364 let shape = parent_frame.shape;
1365 popped_frame.dealloc();
1366 *insert_state = DynamicObjectInsertState::Idle;
1367 // No need to poison - returning Err consumes self, Drop will handle cleanup
1368 return Err(ReflectError::OperationFailed {
1369 shape,
1370 operation: "end() called but object entry value was never initialized",
1371 });
1372 }
1373
1374 // We just popped a value frame, now insert it into the dynamic object
1375 if let Def::DynamicValue(dyn_def) = parent_frame.shape.def {
1376 // Get mutable pointers - both object and value need PtrMut
1377 let object_ptr = unsafe { parent_frame.data.assume_init() };
1378 let value_ptr = unsafe { popped_frame.data.assume_init() };
1379
1380 // Use insert_object_entry to add the key-value pair
1381 unsafe {
1382 (dyn_def.vtable.insert_object_entry)(object_ptr, key, value_ptr);
1383 }
1384
1385 // Insert moved out of popped_frame
1386 popped_frame.tracker = Tracker::Scalar;
1387 popped_frame.is_init = false;
1388 popped_frame.dealloc();
1389
1390 // Reset insert state to Idle
1391 *insert_state = DynamicObjectInsertState::Idle;
1392 }
1393 }
1394 }
1395 _ => {}
1396 }
1397
1398 Ok(self)
1399 }
1400
1401 /// Returns a human-readable path representing the current traversal in the builder,
1402 /// e.g., `RootStruct.fieldName[index].subfield`.
1403 pub fn path(&self) -> String {
1404 let mut out = String::new();
1405
1406 let mut path_components = Vec::new();
1407 // The stack of enum/struct/sequence names currently in context.
1408 // Start from root and build upwards.
1409 for (i, frame) in self.frames().iter().enumerate() {
1410 match frame.shape.ty {
1411 Type::User(user_type) => match user_type {
1412 UserType::Struct(struct_type) => {
1413 // Try to get currently active field index
1414 let mut field_str = None;
1415 if let Tracker::Struct {
1416 current_child: Some(idx),
1417 ..
1418 } = &frame.tracker
1419 && let Some(field) = struct_type.fields.get(*idx)
1420 {
1421 field_str = Some(field.name);
1422 }
1423 if i == 0 {
1424 // Use Display for the root struct shape
1425 path_components.push(format!("{}", frame.shape));
1426 }
1427 if let Some(field_name) = field_str {
1428 path_components.push(format!(".{field_name}"));
1429 }
1430 }
1431 UserType::Enum(_enum_type) => {
1432 // Try to get currently active variant and field
1433 if let Tracker::Enum {
1434 variant,
1435 current_child,
1436 ..
1437 } = &frame.tracker
1438 {
1439 if i == 0 {
1440 // Use Display for the root enum shape
1441 path_components.push(format!("{}", frame.shape));
1442 }
1443 path_components.push(format!("::{}", variant.name));
1444 if let Some(idx) = *current_child
1445 && let Some(field) = variant.data.fields.get(idx)
1446 {
1447 path_components.push(format!(".{}", field.name));
1448 }
1449 } else if i == 0 {
1450 // just the enum display
1451 path_components.push(format!("{}", frame.shape));
1452 }
1453 }
1454 UserType::Union(_union_type) => {
1455 path_components.push(format!("{}", frame.shape));
1456 }
1457 UserType::Opaque => {
1458 path_components.push("<opaque>".to_string());
1459 }
1460 },
1461 Type::Sequence(seq_type) => match seq_type {
1462 facet_core::SequenceType::Array(_array_def) => {
1463 // Try to show current element index
1464 if let Tracker::Array {
1465 current_child: Some(idx),
1466 ..
1467 } = &frame.tracker
1468 {
1469 path_components.push(format!("[{idx}]"));
1470 }
1471 }
1472 // You can add more for Slice, Vec, etc., if applicable
1473 _ => {
1474 // just indicate "[]" for sequence
1475 path_components.push("[]".to_string());
1476 }
1477 },
1478 Type::Pointer(_) => {
1479 // Indicate deref
1480 path_components.push("*".to_string());
1481 }
1482 _ => {
1483 // No structural path
1484 }
1485 }
1486 }
1487 // Merge the path_components into a single string
1488 for component in path_components {
1489 out.push_str(&component);
1490 }
1491 out
1492 }
1493
1494 /// Get the field for the parent frame
1495 pub fn parent_field(&self) -> Option<&Field> {
1496 self.frames()
1497 .iter()
1498 .rev()
1499 .nth(1)
1500 .and_then(|f| f.get_field())
1501 }
1502
1503 /// Gets the field for the current frame
1504 pub fn current_field(&self) -> Option<&Field> {
1505 self.frames().last().and_then(|f| f.get_field())
1506 }
1507}