facet_reflect/partial/partial_api/
maps.rs

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