facet_reflect/partial/partial_api/
lists.rs

1use super::*;
2use crate::AllocatedShape;
3
4////////////////////////////////////////////////////////////////////////////////////////////////////
5// Lists
6////////////////////////////////////////////////////////////////////////////////////////////////////
7impl<const BORROW: bool> Partial<'_, BORROW> {
8    /// Initializes a list (Vec, etc.) if it hasn't been initialized before.
9    /// This is a prerequisite to `begin_push_item`/`set`/`end` or the shorthand
10    /// `push`.
11    ///
12    /// `begin_list` does not clear the list if it was previously initialized.
13    /// `begin_list` does not push a new frame to the stack, and thus does not
14    /// require `end` to be called afterwards.
15    pub fn begin_list(mut self) -> Result<Self, ReflectError> {
16        crate::trace!("begin_list()");
17        let frame = self.frames_mut().last_mut().unwrap();
18
19        match &frame.tracker {
20            Tracker::Scalar if !frame.is_init => {
21                // that's good, let's initialize it
22            }
23            Tracker::Scalar => {
24                // is_init is true - initialized (perhaps from a previous round?) but should be a list tracker
25                // Check what kind of shape we have
26                match &frame.allocated.shape().def {
27                    Def::List(_) => {
28                        // Regular list type - just update the tracker
29                        frame.tracker = Tracker::List {
30                            current_child: false,
31                        };
32                        return Ok(self);
33                    }
34                    Def::DynamicValue(_) => {
35                        // DynamicValue that was already initialized as an array
36                        // Just update the tracker without deinit (preserve existing elements)
37                        frame.tracker = Tracker::DynamicValue {
38                            state: DynamicValueState::Array {
39                                building_element: false,
40                            },
41                        };
42                        return Ok(self);
43                    }
44                    _ => {
45                        return Err(ReflectError::OperationFailed {
46                            shape: frame.allocated.shape(),
47                            operation: "begin_list can only be called on List types or DynamicValue",
48                        });
49                    }
50                }
51            }
52            Tracker::List { .. } => {
53                if frame.is_init {
54                    // already initialized, nothing to do
55                    return Ok(self);
56                }
57            }
58            Tracker::DynamicValue { state } => {
59                // Already initialized as a dynamic array
60                if matches!(state, DynamicValueState::Array { .. }) {
61                    return Ok(self);
62                }
63                // Otherwise (Scalar or other state), we need to deinit before reinitializing.
64                // Must use deinit_for_replace() since we're about to overwrite with a new Array.
65                // This is important for BorrowedInPlace frames where deinit() would early-return
66                // without dropping the existing value.
67                frame.deinit_for_replace();
68            }
69            Tracker::SmartPointerSlice { .. } => {
70                // begin_list is kinda superfluous when we're in a SmartPointerSlice state
71                return Ok(self);
72            }
73            _ => {
74                return Err(ReflectError::UnexpectedTracker {
75                    message: "begin_list called but tracker isn't something list-like",
76                    current_tracker: frame.tracker.kind(),
77                });
78            }
79        };
80
81        // Check that we have a List or DynamicValue
82        match &frame.allocated.shape().def {
83            Def::List(list_def) => {
84                // Check that we have init_in_place_with_capacity function
85                let init_fn = match list_def.init_in_place_with_capacity() {
86                    Some(f) => f,
87                    None => {
88                        return Err(ReflectError::OperationFailed {
89                            shape: frame.allocated.shape(),
90                            operation: "list type does not support initialization with capacity",
91                        });
92                    }
93                };
94
95                // Initialize the list with default capacity (0)
96                unsafe {
97                    init_fn(frame.data, 0);
98                }
99
100                // Update tracker to List state and mark as initialized
101                frame.tracker = Tracker::List {
102                    current_child: false,
103                };
104                frame.is_init = true;
105            }
106            Def::DynamicValue(dyn_def) => {
107                // Initialize as a dynamic array
108                unsafe {
109                    (dyn_def.vtable.begin_array)(frame.data);
110                }
111
112                // Update tracker to DynamicValue array state and mark as initialized
113                frame.tracker = Tracker::DynamicValue {
114                    state: DynamicValueState::Array {
115                        building_element: false,
116                    },
117                };
118                frame.is_init = true;
119            }
120            _ => {
121                return Err(ReflectError::OperationFailed {
122                    shape: frame.allocated.shape(),
123                    operation: "begin_list can only be called on List or DynamicValue types",
124                });
125            }
126        }
127
128        Ok(self)
129    }
130
131    /// Transitions the frame to Array tracker state.
132    ///
133    /// This is used to prepare a fixed-size array for element initialization.
134    /// Unlike `begin_list`, this does not initialize any runtime data - arrays
135    /// are stored inline and don't need a vtable call.
136    ///
137    /// This method is particularly important for zero-length arrays like `[u8; 0]`,
138    /// which have no elements to initialize but still need their tracker state
139    /// to be set correctly for `require_full_initialization` to pass.
140    ///
141    /// `begin_array` does not push a new frame to the stack.
142    pub fn begin_array(mut self) -> Result<Self, ReflectError> {
143        crate::trace!("begin_array()");
144        let frame = self.frames_mut().last_mut().unwrap();
145
146        // Verify this is an array type
147        let array_def = match &frame.allocated.shape().def {
148            Def::Array(array_def) => array_def,
149            _ => {
150                return Err(ReflectError::OperationFailed {
151                    shape: frame.allocated.shape(),
152                    operation: "begin_array can only be called on Array types",
153                });
154            }
155        };
156
157        // Check array size limit
158        if array_def.n > 63 {
159            return Err(ReflectError::OperationFailed {
160                shape: frame.allocated.shape(),
161                operation: "arrays larger than 63 elements are not yet supported",
162            });
163        }
164
165        match &frame.tracker {
166            Tracker::Scalar if !frame.is_init => {
167                // Transition to Array tracker
168                frame.tracker = Tracker::Array {
169                    iset: ISet::default(),
170                    current_child: None,
171                };
172            }
173            Tracker::Array { .. } => {
174                // Already in Array state, nothing to do
175            }
176            _ => {
177                return Err(ReflectError::OperationFailed {
178                    shape: frame.allocated.shape(),
179                    operation: "begin_array: unexpected tracker state",
180                });
181            }
182        }
183
184        Ok(self)
185    }
186
187    /// Pushes an element to the list
188    /// The element should be set using `set()` or similar methods, then `pop()` to complete
189    pub fn begin_list_item(mut self) -> Result<Self, ReflectError> {
190        crate::trace!("begin_list_item()");
191        let frame = self.frames_mut().last_mut().unwrap();
192
193        // Check if we're building a smart pointer slice
194        if let Tracker::SmartPointerSlice {
195            building_item,
196            vtable: _,
197        } = &frame.tracker
198        {
199            if *building_item {
200                return Err(ReflectError::OperationFailed {
201                    shape: frame.allocated.shape(),
202                    operation: "already building an item, call end() first",
203                });
204            }
205
206            // Get the element type from the smart pointer's pointee
207            let element_shape = match &frame.allocated.shape().def {
208                Def::Pointer(smart_ptr_def) => match smart_ptr_def.pointee() {
209                    Some(pointee_shape) => match &pointee_shape.ty {
210                        Type::Sequence(SequenceType::Slice(slice_type)) => slice_type.t,
211                        _ => {
212                            return Err(ReflectError::OperationFailed {
213                                shape: frame.allocated.shape(),
214                                operation: "smart pointer pointee is not a slice",
215                            });
216                        }
217                    },
218                    None => {
219                        return Err(ReflectError::OperationFailed {
220                            shape: frame.allocated.shape(),
221                            operation: "smart pointer has no pointee",
222                        });
223                    }
224                },
225                _ => {
226                    return Err(ReflectError::OperationFailed {
227                        shape: frame.allocated.shape(),
228                        operation: "expected smart pointer definition",
229                    });
230                }
231            };
232
233            // Allocate space for the element
234            crate::trace!("Pointee is a slice of {element_shape}");
235            let element_layout = match element_shape.layout.sized_layout() {
236                Ok(layout) => layout,
237                Err(_) => {
238                    return Err(ReflectError::OperationFailed {
239                        shape: element_shape,
240                        operation: "cannot allocate unsized element",
241                    });
242                }
243            };
244
245            let element_data = if element_layout.size() == 0 {
246                // For ZST, use a non-null but unallocated pointer
247                PtrUninit::new(NonNull::<u8>::dangling().as_ptr())
248            } else {
249                let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
250                let Some(element_ptr) = NonNull::new(element_ptr) else {
251                    return Err(ReflectError::OperationFailed {
252                        shape: frame.allocated.shape(),
253                        operation: "failed to allocate memory for list element",
254                    });
255                };
256                PtrUninit::new(element_ptr.as_ptr())
257            };
258
259            // Create and push the element frame
260            crate::trace!("Pushing element frame, which we just allocated");
261            let element_frame = Frame::new(
262                element_data,
263                AllocatedShape::new(element_shape, element_layout.size()),
264                FrameOwnership::Owned,
265            );
266            self.frames_mut().push(element_frame);
267
268            // Mark that we're building an item
269            // We need to update the tracker after pushing the frame
270            let parent_idx = self.frames().len() - 2;
271            if let Tracker::SmartPointerSlice { building_item, .. } =
272                &mut self.frames_mut()[parent_idx].tracker
273            {
274                crate::trace!("Marking element frame as building item");
275                *building_item = true;
276            }
277
278            return Ok(self);
279        }
280
281        // Check if we're building a DynamicValue array
282        if let Tracker::DynamicValue {
283            state: DynamicValueState::Array { building_element },
284        } = &frame.tracker
285        {
286            if *building_element {
287                return Err(ReflectError::OperationFailed {
288                    shape: frame.allocated.shape(),
289                    operation: "already building an element, call end() first",
290                });
291            }
292
293            // For DynamicValue arrays, the element shape is the same DynamicValue shape
294            // (Value arrays contain Value elements)
295            let element_shape = frame.allocated.shape();
296            let element_layout = match element_shape.layout.sized_layout() {
297                Ok(layout) => layout,
298                Err(_) => {
299                    return Err(ReflectError::Unsized {
300                        shape: element_shape,
301                        operation: "begin_list_item: calculating element layout",
302                    });
303                }
304            };
305
306            let element_data = if element_layout.size() == 0 {
307                // For ZST, use a non-null but unallocated pointer
308                PtrUninit::new(NonNull::<u8>::dangling().as_ptr())
309            } else {
310                let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
311                let Some(element_ptr) = NonNull::new(element_ptr) else {
312                    return Err(ReflectError::OperationFailed {
313                        shape: frame.allocated.shape(),
314                        operation: "failed to allocate memory for list element",
315                    });
316                };
317                PtrUninit::new(element_ptr.as_ptr())
318            };
319
320            // Push a new frame for the element
321            self.frames_mut().push(Frame::new(
322                element_data,
323                AllocatedShape::new(element_shape, element_layout.size()),
324                FrameOwnership::Owned,
325            ));
326
327            // Mark that we're building an element
328            let parent_idx = self.frames().len() - 2;
329            if let Tracker::DynamicValue {
330                state: DynamicValueState::Array { building_element },
331            } = &mut self.frames_mut()[parent_idx].tracker
332            {
333                *building_element = true;
334            }
335
336            return Ok(self);
337        }
338
339        // Check that we have a List that's been initialized
340        let list_def = match &frame.allocated.shape().def {
341            Def::List(list_def) => list_def,
342            _ => {
343                return Err(ReflectError::OperationFailed {
344                    shape: frame.allocated.shape(),
345                    operation: "push can only be called on List or DynamicValue types",
346                });
347            }
348        };
349
350        // Verify the tracker is in List state and initialized
351        match &mut frame.tracker {
352            Tracker::List { current_child } if frame.is_init => {
353                if *current_child {
354                    return Err(ReflectError::OperationFailed {
355                        shape: frame.allocated.shape(),
356                        operation: "already pushing an element, call pop() first",
357                    });
358                }
359                *current_child = true;
360            }
361            _ => {
362                return Err(ReflectError::OperationFailed {
363                    shape: frame.allocated.shape(),
364                    operation: "must call begin_list() before push()",
365                });
366            }
367        }
368
369        // Get the element shape
370        let element_shape = list_def.t();
371
372        // Allocate space for the new element
373        let element_layout = match element_shape.layout.sized_layout() {
374            Ok(layout) => layout,
375            Err(_) => {
376                return Err(ReflectError::Unsized {
377                    shape: element_shape,
378                    operation: "begin_list_item: calculating element layout",
379                });
380            }
381        };
382        let element_data = if element_layout.size() == 0 {
383            // For ZST, use a non-null but unallocated pointer
384            PtrUninit::new(NonNull::<u8>::dangling().as_ptr())
385        } else {
386            let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
387            let Some(element_ptr) = NonNull::new(element_ptr) else {
388                return Err(ReflectError::OperationFailed {
389                    shape: frame.allocated.shape(),
390                    operation: "failed to allocate memory for list element",
391                });
392            };
393            PtrUninit::new(element_ptr.as_ptr())
394        };
395
396        // Push a new frame for the element
397        self.frames_mut().push(Frame::new(
398            element_data,
399            AllocatedShape::new(element_shape, element_layout.size()),
400            FrameOwnership::Owned,
401        ));
402
403        Ok(self)
404    }
405}