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                // First verify this is actually a list type before changing tracker
25                if !matches!(frame.shape.def, Def::List(_)) {
26                    return Err(ReflectError::OperationFailed {
27                        shape: frame.shape,
28                        operation: "begin_list can only be called on List types",
29                    });
30                }
31                frame.tracker = Tracker::List {
32                    current_child: false,
33                };
34                return Ok(self);
35            }
36            Tracker::List { .. } => {
37                if frame.is_init {
38                    // already initialized, nothing to do
39                    return Ok(self);
40                }
41            }
42            Tracker::DynamicValue { state } => {
43                // Already initialized as a dynamic array
44                if matches!(state, DynamicValueState::Array { .. }) {
45                    return Ok(self);
46                }
47                // Otherwise (Scalar or other state), we need to deinit before reinitializing
48                frame.deinit();
49            }
50            Tracker::SmartPointerSlice { .. } => {
51                // begin_list is kinda superfluous when we're in a SmartPointerSlice state
52                return Ok(self);
53            }
54            _ => {
55                return Err(ReflectError::UnexpectedTracker {
56                    message: "begin_list called but tracker isn't something list-like",
57                    current_tracker: frame.tracker.kind(),
58                });
59            }
60        };
61
62        // Check that we have a List or DynamicValue
63        match &frame.shape.def {
64            Def::List(list_def) => {
65                // Check that we have init_in_place_with_capacity function
66                let init_fn = match list_def.init_in_place_with_capacity() {
67                    Some(f) => f,
68                    None => {
69                        return Err(ReflectError::OperationFailed {
70                            shape: frame.shape,
71                            operation: "list type does not support initialization with capacity",
72                        });
73                    }
74                };
75
76                // Initialize the list with default capacity (0)
77                unsafe {
78                    init_fn(frame.data, 0);
79                }
80
81                // Update tracker to List state and mark as initialized
82                frame.tracker = Tracker::List {
83                    current_child: false,
84                };
85                frame.is_init = true;
86            }
87            Def::DynamicValue(dyn_def) => {
88                // Initialize as a dynamic array
89                unsafe {
90                    (dyn_def.vtable.begin_array)(frame.data);
91                }
92
93                // Update tracker to DynamicValue array state and mark as initialized
94                frame.tracker = Tracker::DynamicValue {
95                    state: DynamicValueState::Array {
96                        building_element: false,
97                    },
98                };
99                frame.is_init = true;
100            }
101            _ => {
102                return Err(ReflectError::OperationFailed {
103                    shape: frame.shape,
104                    operation: "begin_list can only be called on List or DynamicValue types",
105                });
106            }
107        }
108
109        Ok(self)
110    }
111
112    /// Pushes an element to the list
113    /// The element should be set using `set()` or similar methods, then `pop()` to complete
114    pub fn begin_list_item(mut self) -> Result<Self, ReflectError> {
115        crate::trace!("begin_list_item()");
116        let frame = self.frames_mut().last_mut().unwrap();
117
118        // Check if we're building a smart pointer slice
119        if let Tracker::SmartPointerSlice {
120            building_item,
121            vtable: _,
122        } = &frame.tracker
123        {
124            if *building_item {
125                return Err(ReflectError::OperationFailed {
126                    shape: frame.shape,
127                    operation: "already building an item, call end() first",
128                });
129            }
130
131            // Get the element type from the smart pointer's pointee
132            let element_shape = match &frame.shape.def {
133                Def::Pointer(smart_ptr_def) => match smart_ptr_def.pointee() {
134                    Some(pointee_shape) => match &pointee_shape.ty {
135                        Type::Sequence(SequenceType::Slice(slice_type)) => slice_type.t,
136                        _ => {
137                            return Err(ReflectError::OperationFailed {
138                                shape: frame.shape,
139                                operation: "smart pointer pointee is not a slice",
140                            });
141                        }
142                    },
143                    None => {
144                        return Err(ReflectError::OperationFailed {
145                            shape: frame.shape,
146                            operation: "smart pointer has no pointee",
147                        });
148                    }
149                },
150                _ => {
151                    return Err(ReflectError::OperationFailed {
152                        shape: frame.shape,
153                        operation: "expected smart pointer definition",
154                    });
155                }
156            };
157
158            // Allocate space for the element
159            crate::trace!("Pointee is a slice of {element_shape}");
160            let element_layout = match element_shape.layout.sized_layout() {
161                Ok(layout) => layout,
162                Err(_) => {
163                    return Err(ReflectError::OperationFailed {
164                        shape: element_shape,
165                        operation: "cannot allocate unsized element",
166                    });
167                }
168            };
169
170            let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
171            let Some(element_ptr) = NonNull::new(element_ptr) else {
172                return Err(ReflectError::OperationFailed {
173                    shape: frame.shape,
174                    operation: "failed to allocate memory for list element",
175                });
176            };
177
178            // Create and push the element frame
179            crate::trace!("Pushing element frame, which we just allocated");
180            let element_frame = Frame::new(
181                PtrUninit::new(element_ptr.as_ptr()),
182                element_shape,
183                FrameOwnership::Owned,
184            );
185            self.frames_mut().push(element_frame);
186
187            // Mark that we're building an item
188            // We need to update the tracker after pushing the frame
189            let parent_idx = self.frames().len() - 2;
190            if let Tracker::SmartPointerSlice { building_item, .. } =
191                &mut self.frames_mut()[parent_idx].tracker
192            {
193                crate::trace!("Marking element frame as building item");
194                *building_item = true;
195            }
196
197            return Ok(self);
198        }
199
200        // Check if we're building a DynamicValue array
201        if let Tracker::DynamicValue {
202            state: DynamicValueState::Array { building_element },
203        } = &frame.tracker
204        {
205            if *building_element {
206                return Err(ReflectError::OperationFailed {
207                    shape: frame.shape,
208                    operation: "already building an element, call end() first",
209                });
210            }
211
212            // For DynamicValue arrays, the element shape is the same DynamicValue shape
213            // (Value arrays contain Value elements)
214            let element_shape = frame.shape;
215            let element_layout = match element_shape.layout.sized_layout() {
216                Ok(layout) => layout,
217                Err(_) => {
218                    return Err(ReflectError::Unsized {
219                        shape: element_shape,
220                        operation: "begin_list_item: calculating element layout",
221                    });
222                }
223            };
224
225            let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
226            let Some(element_ptr) = NonNull::new(element_ptr) else {
227                return Err(ReflectError::OperationFailed {
228                    shape: frame.shape,
229                    operation: "failed to allocate memory for list element",
230                });
231            };
232
233            // Push a new frame for the element
234            self.frames_mut().push(Frame::new(
235                PtrUninit::new(element_ptr.as_ptr()),
236                element_shape,
237                FrameOwnership::Owned,
238            ));
239
240            // Mark that we're building an element
241            let parent_idx = self.frames().len() - 2;
242            if let Tracker::DynamicValue {
243                state: DynamicValueState::Array { building_element },
244            } = &mut self.frames_mut()[parent_idx].tracker
245            {
246                *building_element = true;
247            }
248
249            return Ok(self);
250        }
251
252        // Check that we have a List that's been initialized
253        let list_def = match &frame.shape.def {
254            Def::List(list_def) => list_def,
255            _ => {
256                return Err(ReflectError::OperationFailed {
257                    shape: frame.shape,
258                    operation: "push can only be called on List or DynamicValue types",
259                });
260            }
261        };
262
263        // Verify the tracker is in List state and initialized
264        match &mut frame.tracker {
265            Tracker::List { current_child } if frame.is_init => {
266                if *current_child {
267                    return Err(ReflectError::OperationFailed {
268                        shape: frame.shape,
269                        operation: "already pushing an element, call pop() first",
270                    });
271                }
272                *current_child = true;
273            }
274            _ => {
275                return Err(ReflectError::OperationFailed {
276                    shape: frame.shape,
277                    operation: "must call begin_list() before push()",
278                });
279            }
280        }
281
282        // Get the element shape
283        let element_shape = list_def.t();
284
285        // Allocate space for the new element
286        let element_layout = match element_shape.layout.sized_layout() {
287            Ok(layout) => layout,
288            Err(_) => {
289                return Err(ReflectError::Unsized {
290                    shape: element_shape,
291                    operation: "begin_list_item: calculating element layout",
292                });
293            }
294        };
295        let element_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(element_layout) };
296
297        let Some(element_ptr) = NonNull::new(element_ptr) else {
298            return Err(ReflectError::OperationFailed {
299                shape: frame.shape,
300                operation: "failed to allocate memory for list element",
301            });
302        };
303
304        // Push a new frame for the element
305        self.frames_mut().push(Frame::new(
306            PtrUninit::new(element_ptr.as_ptr()),
307            element_shape,
308            FrameOwnership::Owned,
309        ));
310
311        Ok(self)
312    }
313}