Skip to main content

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 init_map(mut self) -> Result<Self, ReflectError> {
15        // Get shape upfront to avoid borrow conflicts
16        let shape = self.frames().last().unwrap().allocated.shape();
17        let frame = self.mode.stack_mut().last_mut().unwrap();
18
19        // Check tracker state before initializing
20        match &frame.tracker {
21            Tracker::Scalar if !frame.is_init => {
22                // Good, will initialize below
23            }
24            Tracker::Scalar => {
25                // Scalar tracker can mean:
26                // 1. Not yet initialized (is_init = false)
27                // 2. Already initialized from a previous operation (is_init = true)
28                // For case 2, we need to be careful not to overwrite existing values
29                match shape.def {
30                    Def::Map(_) => {
31                        // For Map, just update tracker - the map is already initialized
32                        frame.tracker = Tracker::Map {
33                            insert_state: MapInsertState::Idle,
34                            pending_entries: Vec::new(),
35                            current_entry_index: None,
36                            building_key: false,
37                        };
38                        return Ok(self);
39                    }
40                    Def::DynamicValue(dyn_def) => {
41                        if frame.is_init {
42                            // Value is already initialized. For BorrowedInPlace frames,
43                            // we're pointing to an existing Value in a parent Object.
44                            // Check if the existing value is already an Object - if so,
45                            // just update the tracker and return.
46                            let ptr = unsafe { frame.data.assume_init().as_const() };
47                            let kind = unsafe { (dyn_def.vtable.get_kind)(ptr) };
48                            if kind == facet_core::DynValueKind::Object {
49                                // Already an Object, just update tracker
50                                frame.tracker = Tracker::DynamicValue {
51                                    state: DynamicValueState::Object {
52                                        insert_state: DynamicObjectInsertState::Idle,
53                                        pending_entries: Vec::new(),
54                                    },
55                                };
56                                return Ok(self);
57                            }
58                            // Value is initialized but not an Object - reinitialize.
59                            // Must use deinit_for_replace() to properly drop the old value
60                            // before overwriting, including for BorrowedInPlace frames.
61                            frame.deinit_for_replace();
62                        }
63                        // Fall through to initialize as Object below
64                    }
65                    _ => {
66                        return Err(self.err(ReflectErrorKind::OperationFailed {
67                            shape,
68                            operation: "init_map can only be called on Map or DynamicValue types",
69                        }));
70                    }
71                }
72            }
73            Tracker::Map { .. } => {
74                if frame.is_init {
75                    // Already initialized, nothing to do
76                    return Ok(self);
77                }
78            }
79            Tracker::DynamicValue { state } => {
80                // Already initialized as a dynamic object
81                if matches!(state, DynamicValueState::Object { .. }) {
82                    return Ok(self);
83                }
84                // Otherwise (Scalar or Array state), we need to deinit before reinitializing.
85                // Must use deinit_for_replace() since we're about to overwrite with a new Object.
86                // This is important for BorrowedInPlace frames where deinit() would early-return
87                // without dropping the existing value.
88                frame.deinit_for_replace();
89            }
90            _ => {
91                let tracker_kind = frame.tracker.kind();
92                return Err(self.err(ReflectErrorKind::UnexpectedTracker {
93                    message: "init_map called but tracker isn't Scalar, Map, or DynamicValue",
94                    current_tracker: tracker_kind,
95                }));
96            }
97        }
98
99        // Check that we have a Map or DynamicValue
100        match &shape.def {
101            Def::Map(map_def) => {
102                let init_fn = map_def.vtable.init_in_place_with_capacity;
103
104                // Initialize the map with default capacity (0)
105                // Need to re-borrow frame after the early returns above
106                let frame = self.mode.stack_mut().last_mut().unwrap();
107                unsafe {
108                    init_fn(frame.data, 0);
109                }
110
111                // Update tracker to Map state and mark as initialized
112                frame.tracker = Tracker::Map {
113                    insert_state: MapInsertState::Idle,
114                    pending_entries: Vec::new(),
115                    current_entry_index: None,
116                    building_key: false,
117                };
118                frame.is_init = true;
119            }
120            Def::DynamicValue(dyn_def) => {
121                // Initialize as a dynamic object
122                // Need to re-borrow frame after the early returns above
123                let frame = self.mode.stack_mut().last_mut().unwrap();
124                unsafe {
125                    (dyn_def.vtable.begin_object)(frame.data);
126                }
127
128                // Update tracker to DynamicValue object state and mark as initialized
129                frame.tracker = Tracker::DynamicValue {
130                    state: DynamicValueState::Object {
131                        insert_state: DynamicObjectInsertState::Idle,
132                        pending_entries: Vec::new(),
133                    },
134                };
135                frame.is_init = true;
136            }
137            _ => {
138                return Err(self.err(ReflectErrorKind::OperationFailed {
139                    shape,
140                    operation: "init_map can only be called on Map or DynamicValue types",
141                }));
142            }
143        }
144
145        Ok(self)
146    }
147
148    /// Pushes a frame for the map key. After that, `set()` should be called
149    /// (or the key should be initialized somehow) and `end()` should be called
150    /// to pop the frame.
151    pub fn begin_key(mut self) -> Result<Self, ReflectError> {
152        // Get shape and type_plan upfront to avoid borrow conflicts
153        let frame = self.frames().last().unwrap();
154        let shape = frame.allocated.shape();
155        let parent_type_plan = frame.type_plan;
156        let frame = self.mode.stack_mut().last_mut().unwrap();
157
158        // Check that we have a Map in Idle state
159        let map_def = match (&shape.def, &frame.tracker) {
160            (
161                Def::Map(map_def),
162                Tracker::Map {
163                    insert_state: MapInsertState::Idle,
164                    ..
165                },
166            ) if frame.is_init => map_def,
167            (
168                Def::Map(_),
169                Tracker::Map {
170                    insert_state: MapInsertState::PushingKey { .. },
171                    ..
172                },
173            ) => {
174                return Err(self.err(ReflectErrorKind::OperationFailed {
175                    shape,
176                    operation: "already pushing a key, call end() first",
177                }));
178            }
179            (
180                Def::Map(_),
181                Tracker::Map {
182                    insert_state: MapInsertState::PushingValue { .. },
183                    ..
184                },
185            ) => {
186                return Err(self.err(ReflectErrorKind::OperationFailed {
187                    shape,
188                    operation: "must complete current operation before begin_key()",
189                }));
190            }
191            _ => {
192                return Err(self.err(ReflectErrorKind::OperationFailed {
193                    shape,
194                    operation: "must call init_map() before begin_key()",
195                }));
196            }
197        };
198
199        // Get the key shape
200        let key_shape = map_def.k();
201
202        // Allocate space for the key
203        let key_layout = match key_shape.layout.sized_layout() {
204            Ok(layout) => layout,
205            Err(_) => {
206                return Err(self.err(ReflectErrorKind::Unsized {
207                    shape: key_shape,
208                    operation: "begin_key allocating key",
209                }));
210            }
211        };
212        let key_ptr = facet_core::alloc_for_layout(key_layout);
213
214        // Store the key pointer in the insert state and update entry tracking
215        match &mut frame.tracker {
216            Tracker::Map {
217                insert_state,
218                current_entry_index,
219                building_key,
220                pending_entries,
221            } => {
222                // Increment entry index for new key (starts at 0 for first key)
223                *current_entry_index = Some(match *current_entry_index {
224                    None => pending_entries.len(), // First key starts at current pending count
225                    Some(idx) => idx + 1,
226                });
227                *building_key = true;
228                *insert_state = MapInsertState::PushingKey { key_ptr };
229            }
230            _ => unreachable!(),
231        }
232
233        // Push a new frame for the key
234        // Get child type plan NodeId for map keys
235        let child_plan_id = self
236            .root_plan
237            .map_key_node_id(parent_type_plan)
238            .expect("TypePlan must have map key node");
239        self.mode.stack_mut().push(Frame::new(
240            key_ptr,
241            AllocatedShape::new(key_shape, key_layout.size()),
242            FrameOwnership::TrackedBuffer,
243            child_plan_id,
244        ));
245
246        Ok(self)
247    }
248
249    /// Pushes a frame for the map value
250    /// Must be called after the key has been set and popped
251    pub fn begin_value(mut self) -> Result<Self, ReflectError> {
252        // Get shape and type_plan upfront to avoid borrow conflicts
253        let frame = self.frames().last().unwrap();
254        let shape = frame.allocated.shape();
255        let parent_type_plan = frame.type_plan;
256        let frame = self.mode.stack_mut().last_mut().unwrap();
257
258        // Check that we have a Map in PushingValue state with no value_ptr yet
259        let (map_def, key_ptr) = match (&shape.def, &frame.tracker) {
260            (
261                Def::Map(map_def),
262                Tracker::Map {
263                    insert_state:
264                        MapInsertState::PushingValue {
265                            value_ptr: None,
266                            key_ptr,
267                            ..
268                        },
269                    ..
270                },
271            ) => (map_def, *key_ptr),
272            (
273                Def::Map(_),
274                Tracker::Map {
275                    insert_state:
276                        MapInsertState::PushingValue {
277                            value_ptr: Some(_), ..
278                        },
279                    ..
280                },
281            ) => {
282                return Err(self.err(ReflectErrorKind::OperationFailed {
283                    shape,
284                    operation: "already pushing a value, call end() first",
285                }));
286            }
287            _ => {
288                return Err(self.err(ReflectErrorKind::OperationFailed {
289                    shape,
290                    operation: "must complete key before begin_value()",
291                }));
292            }
293        };
294
295        // Get the value shape
296        let value_shape = map_def.v();
297
298        // Allocate space for the value
299        let value_layout = match value_shape.layout.sized_layout() {
300            Ok(layout) => layout,
301            Err(_) => {
302                return Err(self.err(ReflectErrorKind::Unsized {
303                    shape: value_shape,
304                    operation: "begin_value allocating value",
305                }));
306            }
307        };
308        let value_ptr = facet_core::alloc_for_layout(value_layout);
309
310        // Store the value pointer in the insert state and mark as building value
311        match &mut frame.tracker {
312            Tracker::Map {
313                insert_state,
314                building_key,
315                ..
316            } => {
317                *building_key = false; // Now building value, not key
318                *insert_state = MapInsertState::PushingValue {
319                    key_ptr,
320                    value_ptr: Some(value_ptr),
321                };
322            }
323            _ => unreachable!(),
324        }
325
326        // Push a new frame for the value
327        // Get child type plan NodeId for map values
328        let child_plan_id = self
329            .root_plan
330            .map_value_node_id(parent_type_plan)
331            .expect("TypePlan must have map value node");
332        self.mode.stack_mut().push(Frame::new(
333            value_ptr,
334            AllocatedShape::new(value_shape, value_layout.size()),
335            FrameOwnership::TrackedBuffer,
336            child_plan_id,
337        ));
338
339        Ok(self)
340    }
341
342    /// Begins an object entry for a DynamicValue object.
343    ///
344    /// This is a simpler API than begin_key/begin_value for DynamicValue objects,
345    /// where keys are always strings. The key is stored and a frame is pushed for
346    /// the value. After setting the value and calling `end()`, the key-value pair
347    /// will be inserted into the object.
348    ///
349    /// For `Def::Map` types, use `begin_key()` / `begin_value()` instead.
350    pub fn begin_object_entry(mut self, key: &str) -> Result<Self, ReflectError> {
351        crate::trace!("begin_object_entry({key:?})");
352
353        // Get shape and type_plan upfront to avoid borrow conflicts
354        let frame = self.frames().last().unwrap();
355        let shape = frame.allocated.shape();
356        let parent_type_plan = frame.type_plan;
357        let frame = self.mode.stack_mut().last_mut().unwrap();
358
359        // Check that we have a DynamicValue in Object state with Idle insert_state
360        let dyn_def = match (&shape.def, &frame.tracker) {
361            (
362                Def::DynamicValue(dyn_def),
363                Tracker::DynamicValue {
364                    state:
365                        DynamicValueState::Object {
366                            insert_state: DynamicObjectInsertState::Idle,
367                            ..
368                        },
369                },
370            ) if frame.is_init => {
371                // Good, proceed
372                dyn_def
373            }
374            (
375                Def::DynamicValue(_),
376                Tracker::DynamicValue {
377                    state:
378                        DynamicValueState::Object {
379                            insert_state: DynamicObjectInsertState::BuildingValue { .. },
380                            ..
381                        },
382                },
383            ) => {
384                return Err(self.err(ReflectErrorKind::OperationFailed {
385                    shape,
386                    operation: "already building a value, call end() first",
387                }));
388            }
389            (Def::DynamicValue(_), _) => {
390                return Err(self.err(ReflectErrorKind::OperationFailed {
391                    shape,
392                    operation: "must call init_map() before begin_object_entry()",
393                }));
394            }
395            _ => {
396                return Err(self.err(ReflectErrorKind::OperationFailed {
397                    shape,
398                    operation: "begin_object_entry can only be called on DynamicValue types",
399                }));
400            }
401        };
402
403        // For DynamicValue objects, the value shape is the same DynamicValue shape
404        let value_shape = shape;
405
406        // In deferred mode, check if the key exists in pending_entries first.
407        // This is needed for TOML array-of-tables: [[a.b]] adds to the same array incrementally.
408        // Each [[a.b]] section ends the array temporarily, but subsequent sections should
409        // re-enter and append to the same array, not create a new one.
410        if let Tracker::DynamicValue {
411            state: DynamicValueState::Object {
412                pending_entries, ..
413            },
414        } = &frame.tracker
415            && let Some(idx) = pending_entries.iter().position(|(k, _)| k == key)
416        {
417            let value_ptr = pending_entries[idx].1;
418            let value_size = value_shape
419                .layout
420                .sized_layout()
421                .expect("value must be sized")
422                .size();
423            let child_plan = parent_type_plan;
424            let mut new_frame = Frame::new(
425                value_ptr,
426                AllocatedShape::new(value_shape, value_size),
427                FrameOwnership::BorrowedInPlace,
428                child_plan,
429            );
430            new_frame.is_init = true;
431            // For DynamicValue, we need to check the actual value to set the right tracker.
432            // The value is already initialized, so we set Scalar and let subsequent
433            // operations (init_list, init_map) handle the conversion appropriately.
434            new_frame.tracker = Tracker::Scalar;
435            crate::trace!("begin_object_entry({key:?}): re-entering pending entry at index {idx}");
436            self.mode.stack_mut().push(new_frame);
437            return Ok(self);
438        }
439
440        // Check if key already exists using object_get_mut (for "get or create" semantics)
441        // This is needed for formats like TOML with implicit tables: [a] followed by [a.b.c]
442        if let Some(get_mut_fn) = dyn_def.vtable.object_get_mut {
443            let object_ptr = unsafe { frame.data.assume_init() };
444            if let Some(existing_ptr) = unsafe { get_mut_fn(object_ptr, key) } {
445                // Key exists - push a frame pointing to existing value
446                // Leave insert_state as Idle (no insertion needed on end())
447                // Use ManagedElsewhere since parent object owns this value
448                let value_size = value_shape
449                    .layout
450                    .sized_layout()
451                    .expect("value must be sized")
452                    .size();
453                // For DynamicValue, use the same type plan (self-recursive)
454                let child_plan = parent_type_plan;
455                let mut new_frame = Frame::new(
456                    existing_ptr.as_uninit(),
457                    AllocatedShape::new(value_shape, value_size),
458                    FrameOwnership::BorrowedInPlace,
459                    child_plan,
460                );
461                new_frame.is_init = true;
462                // Set tracker to reflect it's an initialized DynamicValue
463                // For DynamicValue, we need to peek at the value to determine the state.
464                // However, we don't know yet what operations will be called (init_map, init_list, etc.)
465                // So we set Scalar tracker and let init_map/init_list handle the conversion.
466                // init_list will convert Scalar->List if shape is Def::List, or handle DynamicValue directly.
467                new_frame.tracker = Tracker::Scalar;
468                self.mode.stack_mut().push(new_frame);
469                return Ok(self);
470            }
471        }
472
473        // Key doesn't exist - allocate new value
474        let value_layout = match value_shape.layout.sized_layout() {
475            Ok(layout) => layout,
476            Err(_) => {
477                return Err(self.err(ReflectErrorKind::Unsized {
478                    shape: value_shape,
479                    operation: "begin_object_entry: calculating value layout",
480                }));
481            }
482        };
483
484        let value_ptr = facet_core::alloc_for_layout(value_layout);
485
486        // Update the insert state with the key
487        match &mut frame.tracker {
488            Tracker::DynamicValue {
489                state: DynamicValueState::Object { insert_state, .. },
490            } => {
491                *insert_state = DynamicObjectInsertState::BuildingValue {
492                    key: String::from(key),
493                };
494            }
495            _ => unreachable!(),
496        }
497
498        // Push a new frame for the value
499        // For DynamicValue, use the same type plan (self-recursive)
500        let child_plan = parent_type_plan;
501        self.mode.stack_mut().push(Frame::new(
502            value_ptr,
503            AllocatedShape::new(value_shape, value_layout.size()),
504            FrameOwnership::Owned,
505            child_plan,
506        ));
507
508        Ok(self)
509    }
510}