facet_reflect/partial/partial_api/
maps.rs

1use super::*;
2use crate::AllocatedShape;
3
4////////////////////////////////////////////////////////////////////////////////////////////////////
5// Maps
6////////////////////////////////////////////////////////////////////////////////////////////////////
7impl<const BORROW: bool> Partial<'_, BORROW> {
8    /// Begins a map initialization operation
9    ///
10    /// This initializes the map with default capacity and allows inserting key-value pairs
11    /// It does _not_ push a new frame onto the stack.
12    ///
13    /// For `Def::DynamicValue` types, this initializes as an object instead of a map.
14    pub fn begin_map(mut self) -> Result<Self, ReflectError> {
15        let frame = self.frames_mut().last_mut().unwrap();
16
17        // Check tracker state before initializing
18        match &frame.tracker {
19            Tracker::Scalar if !frame.is_init => {
20                // Good, will initialize below
21            }
22            Tracker::Scalar => {
23                // Scalar tracker can mean:
24                // 1. Not yet initialized (is_init = false)
25                // 2. Already initialized from a previous operation (is_init = true)
26                // For case 2, we need to be careful not to overwrite existing values
27                match frame.allocated.shape().def {
28                    Def::Map(_) => {
29                        // For Map, just update tracker - the map is already initialized
30                        frame.tracker = Tracker::Map {
31                            insert_state: MapInsertState::Idle,
32                        };
33                        return Ok(self);
34                    }
35                    Def::DynamicValue(dyn_def) => {
36                        if frame.is_init {
37                            // Value is already initialized. For BorrowedInPlace frames,
38                            // we're pointing to an existing Value in a parent Object.
39                            // Check if the existing value is already an Object - if so,
40                            // just update the tracker and return.
41                            let ptr = unsafe { frame.data.assume_init().as_const() };
42                            let kind = unsafe { (dyn_def.vtable.get_kind)(ptr) };
43                            if kind == facet_core::DynValueKind::Object {
44                                // Already an Object, just update tracker
45                                frame.tracker = Tracker::DynamicValue {
46                                    state: DynamicValueState::Object {
47                                        insert_state: DynamicObjectInsertState::Idle,
48                                    },
49                                };
50                                return Ok(self);
51                            }
52                            // Value is initialized but not an Object - reinitialize.
53                            // Must use deinit_for_replace() to properly drop the old value
54                            // before overwriting, including for BorrowedInPlace frames.
55                            frame.deinit_for_replace();
56                        }
57                        // Fall through to initialize as Object below
58                    }
59                    _ => {
60                        return Err(ReflectError::OperationFailed {
61                            shape: frame.allocated.shape(),
62                            operation: "begin_map can only be called on Map or DynamicValue types",
63                        });
64                    }
65                }
66            }
67            Tracker::Map { .. } => {
68                if frame.is_init {
69                    // Already initialized, nothing to do
70                    return Ok(self);
71                }
72            }
73            Tracker::DynamicValue { state } => {
74                // Already initialized as a dynamic object
75                if matches!(state, DynamicValueState::Object { .. }) {
76                    return Ok(self);
77                }
78                // Otherwise (Scalar or Array state), we need to deinit before reinitializing.
79                // Must use deinit_for_replace() since we're about to overwrite with a new Object.
80                // This is important for BorrowedInPlace frames where deinit() would early-return
81                // without dropping the existing value.
82                frame.deinit_for_replace();
83            }
84            _ => {
85                return Err(ReflectError::UnexpectedTracker {
86                    message: "begin_map called but tracker isn't Scalar, Map, or DynamicValue",
87                    current_tracker: frame.tracker.kind(),
88                });
89            }
90        }
91
92        // Check that we have a Map or DynamicValue
93        match &frame.allocated.shape().def {
94            Def::Map(map_def) => {
95                let init_fn = map_def.vtable.init_in_place_with_capacity;
96
97                // Initialize the map with default capacity (0)
98                unsafe {
99                    init_fn(frame.data, 0);
100                }
101
102                // Update tracker to Map state and mark as initialized
103                frame.tracker = Tracker::Map {
104                    insert_state: MapInsertState::Idle,
105                };
106                frame.is_init = true;
107            }
108            Def::DynamicValue(dyn_def) => {
109                // Initialize as a dynamic object
110                unsafe {
111                    (dyn_def.vtable.begin_object)(frame.data);
112                }
113
114                // Update tracker to DynamicValue object state and mark as initialized
115                frame.tracker = Tracker::DynamicValue {
116                    state: DynamicValueState::Object {
117                        insert_state: DynamicObjectInsertState::Idle,
118                    },
119                };
120                frame.is_init = true;
121            }
122            _ => {
123                return Err(ReflectError::OperationFailed {
124                    shape: frame.allocated.shape(),
125                    operation: "begin_map can only be called on Map or DynamicValue types",
126                });
127            }
128        }
129
130        Ok(self)
131    }
132
133    /// Pushes a frame for the map key. After that, `set()` should be called
134    /// (or the key should be initialized somehow) and `end()` should be called
135    /// to pop the frame.
136    pub fn begin_key(mut self) -> Result<Self, ReflectError> {
137        let frame = self.frames_mut().last_mut().unwrap();
138
139        // Check that we have a Map in Idle state
140        let map_def = match (&frame.allocated.shape().def, &frame.tracker) {
141            (
142                Def::Map(map_def),
143                Tracker::Map {
144                    insert_state: MapInsertState::Idle,
145                },
146            ) if frame.is_init => map_def,
147            (
148                Def::Map(_),
149                Tracker::Map {
150                    insert_state: MapInsertState::PushingKey { .. },
151                },
152            ) => {
153                return Err(ReflectError::OperationFailed {
154                    shape: frame.allocated.shape(),
155                    operation: "already pushing a key, call end() first",
156                });
157            }
158            (
159                Def::Map(_),
160                Tracker::Map {
161                    insert_state: MapInsertState::PushingValue { .. },
162                },
163            ) => {
164                return Err(ReflectError::OperationFailed {
165                    shape: frame.allocated.shape(),
166                    operation: "must complete current operation before begin_key()",
167                });
168            }
169            _ => {
170                return Err(ReflectError::OperationFailed {
171                    shape: frame.allocated.shape(),
172                    operation: "must call begin_map() before begin_key()",
173                });
174            }
175        };
176
177        // Get the key shape
178        let key_shape = map_def.k();
179
180        // Allocate space for the key
181        let key_layout = match key_shape.layout.sized_layout() {
182            Ok(layout) => layout,
183            Err(_) => {
184                return Err(ReflectError::Unsized {
185                    shape: key_shape,
186                    operation: "begin_key allocating key",
187                });
188            }
189        };
190        let key_ptr_raw: *mut u8 = unsafe { ::alloc::alloc::alloc(key_layout) };
191
192        let Some(key_ptr_raw) = NonNull::new(key_ptr_raw) else {
193            return Err(ReflectError::OperationFailed {
194                shape: frame.allocated.shape(),
195                operation: "failed to allocate memory for map key",
196            });
197        };
198
199        let key_ptr = PtrUninit::new(key_ptr_raw.as_ptr());
200
201        // Store the key pointer in the insert state
202        match &mut frame.tracker {
203            Tracker::Map { insert_state, .. } => {
204                *insert_state = MapInsertState::PushingKey {
205                    key_ptr,
206                    key_initialized: false,
207                    key_frame_on_stack: true, // TrackedBuffer frame is now on the stack
208                };
209            }
210            _ => unreachable!(),
211        }
212
213        // Push a new frame for the key
214        self.frames_mut().push(Frame::new(
215            PtrUninit::new(key_ptr_raw.as_ptr()),
216            AllocatedShape::new(key_shape, key_layout.size()),
217            FrameOwnership::TrackedBuffer,
218        ));
219
220        Ok(self)
221    }
222
223    /// Pushes a frame for the map value
224    /// Must be called after the key has been set and popped
225    pub fn begin_value(mut self) -> Result<Self, ReflectError> {
226        let frame = self.frames_mut().last_mut().unwrap();
227
228        // Check that we have a Map in PushingValue state with no value_ptr yet
229        let (map_def, key_ptr) = match (&frame.allocated.shape().def, &frame.tracker) {
230            (
231                Def::Map(map_def),
232                Tracker::Map {
233                    insert_state:
234                        MapInsertState::PushingValue {
235                            value_ptr: None,
236                            key_ptr,
237                            ..
238                        },
239                    ..
240                },
241            ) => (map_def, *key_ptr),
242            (
243                Def::Map(_),
244                Tracker::Map {
245                    insert_state:
246                        MapInsertState::PushingValue {
247                            value_ptr: Some(_), ..
248                        },
249                    ..
250                },
251            ) => {
252                return Err(ReflectError::OperationFailed {
253                    shape: frame.allocated.shape(),
254                    operation: "already pushing a value, call end() first",
255                });
256            }
257            _ => {
258                return Err(ReflectError::OperationFailed {
259                    shape: frame.allocated.shape(),
260                    operation: "must complete key before begin_value()",
261                });
262            }
263        };
264
265        // Get the value shape
266        let value_shape = map_def.v();
267
268        // Allocate space for the value
269        let value_layout = match value_shape.layout.sized_layout() {
270            Ok(layout) => layout,
271            Err(_) => {
272                return Err(ReflectError::Unsized {
273                    shape: value_shape,
274                    operation: "begin_value allocating value",
275                });
276            }
277        };
278        let value_ptr_raw: *mut u8 = unsafe { ::alloc::alloc::alloc(value_layout) };
279
280        let Some(value_ptr_raw) = NonNull::new(value_ptr_raw) else {
281            return Err(ReflectError::OperationFailed {
282                shape: frame.allocated.shape(),
283                operation: "failed to allocate memory for map value",
284            });
285        };
286
287        let value_ptr = PtrUninit::new(value_ptr_raw.as_ptr());
288
289        // Store the value pointer in the insert state
290        match &mut frame.tracker {
291            Tracker::Map { insert_state, .. } => {
292                *insert_state = MapInsertState::PushingValue {
293                    key_ptr,
294                    value_ptr: Some(value_ptr),
295                    value_initialized: false,
296                    value_frame_on_stack: true, // TrackedBuffer frame is now on the stack
297                };
298            }
299            _ => unreachable!(),
300        }
301
302        // Push a new frame for the value
303        self.frames_mut().push(Frame::new(
304            value_ptr,
305            AllocatedShape::new(value_shape, value_layout.size()),
306            FrameOwnership::TrackedBuffer,
307        ));
308
309        Ok(self)
310    }
311
312    /// Begins an object entry for a DynamicValue object.
313    ///
314    /// This is a simpler API than begin_key/begin_value for DynamicValue objects,
315    /// where keys are always strings. The key is stored and a frame is pushed for
316    /// the value. After setting the value and calling `end()`, the key-value pair
317    /// will be inserted into the object.
318    ///
319    /// For `Def::Map` types, use `begin_key()` / `begin_value()` instead.
320    pub fn begin_object_entry(mut self, key: &str) -> Result<Self, ReflectError> {
321        crate::trace!("begin_object_entry({key:?})");
322        let frame = self.frames_mut().last_mut().unwrap();
323
324        // Check that we have a DynamicValue in Object state with Idle insert_state
325        let dyn_def = match (&frame.allocated.shape().def, &frame.tracker) {
326            (
327                Def::DynamicValue(dyn_def),
328                Tracker::DynamicValue {
329                    state:
330                        DynamicValueState::Object {
331                            insert_state: DynamicObjectInsertState::Idle,
332                        },
333                },
334            ) if frame.is_init => {
335                // Good, proceed
336                dyn_def
337            }
338            (
339                Def::DynamicValue(_),
340                Tracker::DynamicValue {
341                    state:
342                        DynamicValueState::Object {
343                            insert_state: DynamicObjectInsertState::BuildingValue { .. },
344                        },
345                },
346            ) => {
347                return Err(ReflectError::OperationFailed {
348                    shape: frame.allocated.shape(),
349                    operation: "already building a value, call end() first",
350                });
351            }
352            (Def::DynamicValue(_), _) => {
353                return Err(ReflectError::OperationFailed {
354                    shape: frame.allocated.shape(),
355                    operation: "must call begin_map() before begin_object_entry()",
356                });
357            }
358            _ => {
359                return Err(ReflectError::OperationFailed {
360                    shape: frame.allocated.shape(),
361                    operation: "begin_object_entry can only be called on DynamicValue types",
362                });
363            }
364        };
365
366        // For DynamicValue objects, the value shape is the same DynamicValue shape
367        let value_shape = frame.allocated.shape();
368
369        // Check if key already exists using object_get_mut (for "get or create" semantics)
370        // This is needed for formats like TOML with implicit tables: [a] followed by [a.b.c]
371        if let Some(get_mut_fn) = dyn_def.vtable.object_get_mut {
372            let object_ptr = unsafe { frame.data.assume_init() };
373            if let Some(existing_ptr) = unsafe { get_mut_fn(object_ptr, key) } {
374                // Key exists - push a frame pointing to existing value
375                // Leave insert_state as Idle (no insertion needed on end())
376                // Use ManagedElsewhere since parent object owns this value
377                let value_size = value_shape
378                    .layout
379                    .sized_layout()
380                    .expect("value must be sized")
381                    .size();
382                let mut new_frame = Frame::new(
383                    existing_ptr.as_uninit(),
384                    AllocatedShape::new(value_shape, value_size),
385                    FrameOwnership::BorrowedInPlace,
386                );
387                new_frame.is_init = true;
388                // Set tracker to reflect it's an initialized DynamicValue
389                // For DynamicValue, we need to peek at the value to determine the state.
390                // However, we don't know yet what operations will be called (begin_map, begin_list, etc.)
391                // So we set Scalar tracker and let begin_map/begin_list handle the conversion.
392                // begin_list will convert Scalar->List if shape is Def::List, or handle DynamicValue directly.
393                new_frame.tracker = Tracker::Scalar;
394                self.frames_mut().push(new_frame);
395                return Ok(self);
396            }
397        }
398
399        // Key doesn't exist - allocate new value
400        let value_layout = match value_shape.layout.sized_layout() {
401            Ok(layout) => layout,
402            Err(_) => {
403                return Err(ReflectError::Unsized {
404                    shape: value_shape,
405                    operation: "begin_object_entry: calculating value layout",
406                });
407            }
408        };
409
410        let value_ptr: *mut u8 = unsafe { ::alloc::alloc::alloc(value_layout) };
411        let Some(value_ptr) = NonNull::new(value_ptr) else {
412            return Err(ReflectError::OperationFailed {
413                shape: frame.allocated.shape(),
414                operation: "failed to allocate memory for object value",
415            });
416        };
417
418        // Update the insert state with the key
419        match &mut frame.tracker {
420            Tracker::DynamicValue {
421                state: DynamicValueState::Object { insert_state },
422            } => {
423                *insert_state = DynamicObjectInsertState::BuildingValue {
424                    key: String::from(key),
425                };
426            }
427            _ => unreachable!(),
428        }
429
430        // Push a new frame for the value
431        self.frames_mut().push(Frame::new(
432            PtrUninit::new(value_ptr.as_ptr()),
433            AllocatedShape::new(value_shape, value_layout.size()),
434            FrameOwnership::Owned,
435        ));
436
437        Ok(self)
438    }
439}