Skip to main content

facet_reflect/partial/partial_api/
build.rs

1use super::*;
2
3////////////////////////////////////////////////////////////////////////////////////////////////////
4// Build
5////////////////////////////////////////////////////////////////////////////////////////////////////
6impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
7    /// Builds the value, consuming the Partial.
8    pub fn build(mut self) -> Result<HeapValue<'facet, BORROW>, ReflectError> {
9        if self.frames().len() != 1 {
10            return Err(ReflectError::InvariantViolation {
11                invariant: "Partial::build() expects a single frame — call end() until that's the case",
12            });
13        }
14
15        let frame = self.frames_mut().last_mut().unwrap();
16
17        // Fill in defaults for any unset fields before checking initialization
18        crate::trace!(
19            "build(): calling fill_defaults for {}, tracker={:?}, is_init={}",
20            frame.allocated.shape(),
21            frame.tracker.kind(),
22            frame.is_init
23        );
24        frame.fill_defaults()?;
25        crate::trace!(
26            "build(): after fill_defaults, tracker={:?}, is_init={}",
27            frame.tracker.kind(),
28            frame.is_init
29        );
30
31        let frame = self.frames_mut().pop().unwrap();
32
33        // Check initialization before proceeding
34        crate::trace!(
35            "build(): calling require_full_initialization, tracker={:?}",
36            frame.tracker.kind()
37        );
38        let init_result = frame.require_full_initialization();
39        crate::trace!(
40            "build(): require_full_initialization returned {:?}",
41            init_result.is_ok()
42        );
43        if let Err(e) = init_result {
44            // Put the frame back so Drop can handle cleanup properly
45            self.frames_mut().push(frame);
46            return Err(e);
47        }
48
49        // Check invariants if present
50        // Safety: The value is fully initialized at this point (we just checked with require_full_initialization)
51        let value_ptr = unsafe { frame.data.assume_init().as_const() };
52        if let Some(result) = unsafe { frame.allocated.shape().call_invariants(value_ptr) } {
53            match result {
54                Ok(()) => {
55                    // Invariants passed
56                }
57                Err(message) => {
58                    // Put the frame back so Drop can handle cleanup properly
59                    let shape = frame.allocated.shape();
60                    self.frames_mut().push(frame);
61                    return Err(ReflectError::UserInvariantFailed { message, shape });
62                }
63            }
64        }
65
66        // Mark as built to prevent Drop from cleaning up the value
67        self.state = PartialState::Built;
68
69        match frame
70            .allocated
71            .shape()
72            .layout
73            .sized_layout()
74            .map_err(|_layout_err| ReflectError::Unsized {
75                shape: frame.allocated.shape(),
76                operation: "build (final check for sized layout)",
77            }) {
78            Ok(layout) => {
79                // Determine if we should deallocate based on ownership
80                let should_dealloc = frame.ownership.needs_dealloc();
81
82                Ok(HeapValue {
83                    guard: Some(Guard {
84                        ptr: unsafe { NonNull::new_unchecked(frame.data.as_mut_byte_ptr()) },
85                        layout,
86                        should_dealloc,
87                    }),
88                    shape: frame.allocated.shape(),
89                    phantom: PhantomData,
90                })
91            }
92            Err(e) => {
93                // Put the frame back for proper cleanup
94                self.frames_mut().push(frame);
95                Err(e)
96            }
97        }
98    }
99
100    /// Finishes deserialization in-place, validating the value without moving it.
101    ///
102    /// This is intended for use with [`from_raw`](Self::from_raw) where the value
103    /// is deserialized into caller-provided memory (e.g., a `MaybeUninit<T>` on the stack).
104    ///
105    /// On success, the caller can safely assume the memory contains a fully initialized,
106    /// valid value and call `MaybeUninit::assume_init()`.
107    ///
108    /// On failure, any partially initialized data is cleaned up (dropped), and the
109    /// memory should be considered uninitialized.
110    ///
111    /// # Panics
112    ///
113    /// Panics if called with more than one frame on the stack (i.e., if you haven't
114    /// called `end()` enough times to return to the root level).
115    ///
116    /// # Example
117    ///
118    /// ```ignore
119    /// use std::mem::MaybeUninit;
120    /// use facet_core::{Facet, PtrUninit};
121    /// use facet_reflect::Partial;
122    ///
123    /// let mut slot = MaybeUninit::<MyStruct>::uninit();
124    /// let ptr = PtrUninit::new(slot.as_mut_ptr().cast());
125    ///
126    /// let partial = unsafe { Partial::from_raw(ptr, MyStruct::SHAPE)? };
127    /// // ... deserialize into partial ...
128    /// partial.finish_in_place()?;
129    ///
130    /// // Now safe to assume initialized
131    /// let value = unsafe { slot.assume_init() };
132    /// ```
133    pub fn finish_in_place(mut self) -> Result<(), ReflectError> {
134        if self.frames().len() != 1 {
135            return Err(ReflectError::InvariantViolation {
136                invariant: "Partial::finish_in_place() expects a single frame — call end() until that's the case",
137            });
138        }
139
140        let frame = self.frames_mut().last_mut().unwrap();
141
142        // Fill in defaults for any unset fields before checking initialization
143        crate::trace!(
144            "finish_in_place(): calling fill_defaults for {}, tracker={:?}, is_init={}",
145            frame.allocated.shape(),
146            frame.tracker.kind(),
147            frame.is_init
148        );
149        frame.fill_defaults()?;
150        crate::trace!(
151            "finish_in_place(): after fill_defaults, tracker={:?}, is_init={}",
152            frame.tracker.kind(),
153            frame.is_init
154        );
155
156        let frame = self.frames_mut().pop().unwrap();
157
158        // Check initialization before proceeding
159        crate::trace!(
160            "finish_in_place(): calling require_full_initialization, tracker={:?}",
161            frame.tracker.kind()
162        );
163        let init_result = frame.require_full_initialization();
164        crate::trace!(
165            "finish_in_place(): require_full_initialization returned {:?}",
166            init_result.is_ok()
167        );
168        if let Err(e) = init_result {
169            // Put the frame back so Drop can handle cleanup properly
170            self.frames_mut().push(frame);
171            return Err(e);
172        }
173
174        // Check invariants if present
175        // Safety: The value is fully initialized at this point (we just checked with require_full_initialization)
176        let value_ptr = unsafe { frame.data.assume_init().as_const() };
177        if let Some(result) = unsafe { frame.allocated.shape().call_invariants(value_ptr) } {
178            match result {
179                Ok(()) => {
180                    // Invariants passed
181                }
182                Err(message) => {
183                    // Put the frame back so Drop can handle cleanup properly
184                    let shape = frame.allocated.shape();
185                    self.frames_mut().push(frame);
186                    return Err(ReflectError::UserInvariantFailed { message, shape });
187                }
188            }
189        }
190
191        // Mark as built to prevent Drop from cleaning up the now-valid value.
192        // The caller owns the memory and will handle the value from here.
193        self.state = PartialState::Built;
194
195        // Frame is dropped here without deallocation (External ownership doesn't dealloc)
196        Ok(())
197    }
198}