facet_reflect/partial/partial_api/
lists.rs

1use super::*;
2
3////////////////////////////////////////////////////////////////////////////////////////////////////
4// Lists
5////////////////////////////////////////////////////////////////////////////////////////////////////
6impl<const BORROW: bool> Partial<'_, BORROW> {
7    /// Initializes a list (Vec, etc.) if it hasn't been initialized before.
8    /// This is a prerequisite to `begin_push_item`/`set`/`end` or the shorthand
9    /// `push`.
10    ///
11    /// `begin_list` does not clear the list if it was previously initialized.
12    /// `begin_list` does not push a new frame to the stack, and thus does not
13    /// require `end` to be called afterwards.
14    pub fn begin_list(mut self) -> Result<Self, ReflectError> {
15        crate::trace!("begin_list()");
16        let frame = self.frames_mut().last_mut().unwrap();
17
18        match &frame.tracker {
19            Tracker::Scalar if !frame.is_init => {
20                // that's good, let's initialize it
21            }
22            Tracker::Scalar => {
23                // is_init is true - initialized (perhaps from a previous round?) but should be a list tracker
24                // Check what kind of shape we have
25                match &frame.shape.def {
26                    Def::List(_) => {
27                        // Regular list type - just update the tracker
28                        frame.tracker = Tracker::List {
29                            current_child: false,
30                        };
31                        return Ok(self);
32                    }
33                    Def::DynamicValue(_) => {
34                        // DynamicValue that was already initialized as an array
35                        // Just update the tracker without deinit (preserve existing elements)
36                        frame.tracker = Tracker::DynamicValue {
37                            state: DynamicValueState::Array {
38                                building_element: false,
39                            },
40                        };
41                        return Ok(self);
42                    }
43                    _ => {
44                        return Err(ReflectError::OperationFailed {
45                            shape: frame.shape,
46                            operation: "begin_list can only be called on List types or DynamicValue",
47                        });
48                    }
49                }
50            }
51            Tracker::List { .. } => {
52                if frame.is_init {
53                    // already initialized, nothing to do
54                    return Ok(self);
55                }
56            }
57            Tracker::DynamicValue { state } => {
58                // Already initialized as a dynamic array
59                if matches!(state, DynamicValueState::Array { .. }) {
60                    return Ok(self);
61                }
62                // Otherwise (Scalar or other state), we need to deinit before reinitializing.
63                // For ManagedElsewhere frames, deinit() skips dropping, so drop explicitly.
64                if matches!(frame.ownership, FrameOwnership::ManagedElsewhere) && frame.is_init {
65                    unsafe { frame.shape.call_drop_in_place(frame.data.assume_init()) };
66                }
67                frame.deinit();
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.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.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.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.shape.def {
148            Def::Array(array_def) => array_def,
149            _ => {
150                return Err(ReflectError::OperationFailed {
151                    shape: frame.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.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.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.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.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.shape,
214                                operation: "smart pointer pointee is not a slice",
215                            });
216                        }
217                    },
218                    None => {
219                        return Err(ReflectError::OperationFailed {
220                            shape: frame.shape,
221                            operation: "smart pointer has no pointee",
222                        });
223                    }
224                },
225                _ => {
226                    return Err(ReflectError::OperationFailed {
227                        shape: frame.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.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(element_data, element_shape, FrameOwnership::Owned);
262            self.frames_mut().push(element_frame);
263
264            // Mark that we're building an item
265            // We need to update the tracker after pushing the frame
266            let parent_idx = self.frames().len() - 2;
267            if let Tracker::SmartPointerSlice { building_item, .. } =
268                &mut self.frames_mut()[parent_idx].tracker
269            {
270                crate::trace!("Marking element frame as building item");
271                *building_item = true;
272            }
273
274            return Ok(self);
275        }
276
277        // Check if we're building a DynamicValue array
278        if let Tracker::DynamicValue {
279            state: DynamicValueState::Array { building_element },
280        } = &frame.tracker
281        {
282            if *building_element {
283                return Err(ReflectError::OperationFailed {
284                    shape: frame.shape,
285                    operation: "already building an element, call end() first",
286                });
287            }
288
289            // For DynamicValue arrays, the element shape is the same DynamicValue shape
290            // (Value arrays contain Value elements)
291            let element_shape = frame.shape;
292            let element_layout = match element_shape.layout.sized_layout() {
293                Ok(layout) => layout,
294                Err(_) => {
295                    return Err(ReflectError::Unsized {
296                        shape: element_shape,
297                        operation: "begin_list_item: calculating element layout",
298                    });
299                }
300            };
301
302            let element_data = if element_layout.size() == 0 {
303                // For ZST, use a non-null but unallocated pointer
304                PtrUninit::new(NonNull::<u8>::dangling().as_ptr())
305            } else {
306                let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
307                let Some(element_ptr) = NonNull::new(element_ptr) else {
308                    return Err(ReflectError::OperationFailed {
309                        shape: frame.shape,
310                        operation: "failed to allocate memory for list element",
311                    });
312                };
313                PtrUninit::new(element_ptr.as_ptr())
314            };
315
316            // Push a new frame for the element
317            self.frames_mut().push(Frame::new(
318                element_data,
319                element_shape,
320                FrameOwnership::Owned,
321            ));
322
323            // Mark that we're building an element
324            let parent_idx = self.frames().len() - 2;
325            if let Tracker::DynamicValue {
326                state: DynamicValueState::Array { building_element },
327            } = &mut self.frames_mut()[parent_idx].tracker
328            {
329                *building_element = true;
330            }
331
332            return Ok(self);
333        }
334
335        // Check that we have a List that's been initialized
336        let list_def = match &frame.shape.def {
337            Def::List(list_def) => list_def,
338            _ => {
339                return Err(ReflectError::OperationFailed {
340                    shape: frame.shape,
341                    operation: "push can only be called on List or DynamicValue types",
342                });
343            }
344        };
345
346        // Verify the tracker is in List state and initialized
347        match &mut frame.tracker {
348            Tracker::List { current_child } if frame.is_init => {
349                if *current_child {
350                    return Err(ReflectError::OperationFailed {
351                        shape: frame.shape,
352                        operation: "already pushing an element, call pop() first",
353                    });
354                }
355                *current_child = true;
356            }
357            _ => {
358                return Err(ReflectError::OperationFailed {
359                    shape: frame.shape,
360                    operation: "must call begin_list() before push()",
361                });
362            }
363        }
364
365        // Get the element shape
366        let element_shape = list_def.t();
367
368        // Allocate space for the new element
369        let element_layout = match element_shape.layout.sized_layout() {
370            Ok(layout) => layout,
371            Err(_) => {
372                return Err(ReflectError::Unsized {
373                    shape: element_shape,
374                    operation: "begin_list_item: calculating element layout",
375                });
376            }
377        };
378        let element_data = if element_layout.size() == 0 {
379            // For ZST, use a non-null but unallocated pointer
380            PtrUninit::new(NonNull::<u8>::dangling().as_ptr())
381        } else {
382            let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
383            let Some(element_ptr) = NonNull::new(element_ptr) else {
384                return Err(ReflectError::OperationFailed {
385                    shape: frame.shape,
386                    operation: "failed to allocate memory for list element",
387                });
388            };
389            PtrUninit::new(element_ptr.as_ptr())
390        };
391
392        // Push a new frame for the element
393        self.frames_mut().push(Frame::new(
394            element_data,
395            element_shape,
396            FrameOwnership::Owned,
397        ));
398
399        Ok(self)
400    }
401}