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 {
229                    key_ptr,
230                    key_initialized: false,
231                    key_frame_on_stack: true, // TrackedBuffer frame is now on the stack
232                };
233            }
234            _ => unreachable!(),
235        }
236
237        // Push a new frame for the key
238        // Get child type plan NodeId for map keys
239        let child_plan_id = self
240            .root_plan
241            .map_key_node_id(parent_type_plan)
242            .expect("TypePlan must have map key node");
243        self.mode.stack_mut().push(Frame::new(
244            key_ptr,
245            AllocatedShape::new(key_shape, key_layout.size()),
246            FrameOwnership::TrackedBuffer,
247            child_plan_id,
248        ));
249
250        Ok(self)
251    }
252
253    /// Pushes a frame for the map value
254    /// Must be called after the key has been set and popped
255    pub fn begin_value(mut self) -> Result<Self, ReflectError> {
256        // Get shape and type_plan upfront to avoid borrow conflicts
257        let frame = self.frames().last().unwrap();
258        let shape = frame.allocated.shape();
259        let parent_type_plan = frame.type_plan;
260        let frame = self.mode.stack_mut().last_mut().unwrap();
261
262        // Check that we have a Map in PushingValue state with no value_ptr yet
263        let (map_def, key_ptr, key_frame_stored) = match (&shape.def, &frame.tracker) {
264            (
265                Def::Map(map_def),
266                Tracker::Map {
267                    insert_state:
268                        MapInsertState::PushingValue {
269                            value_ptr: None,
270                            key_ptr,
271                            key_frame_stored,
272                            ..
273                        },
274                    ..
275                },
276            ) => (map_def, *key_ptr, *key_frame_stored),
277            (
278                Def::Map(_),
279                Tracker::Map {
280                    insert_state:
281                        MapInsertState::PushingValue {
282                            value_ptr: Some(_), ..
283                        },
284                    ..
285                },
286            ) => {
287                return Err(self.err(ReflectErrorKind::OperationFailed {
288                    shape,
289                    operation: "already pushing a value, call end() first",
290                }));
291            }
292            _ => {
293                return Err(self.err(ReflectErrorKind::OperationFailed {
294                    shape,
295                    operation: "must complete key before begin_value()",
296                }));
297            }
298        };
299
300        // Get the value shape
301        let value_shape = map_def.v();
302
303        // Allocate space for the value
304        let value_layout = match value_shape.layout.sized_layout() {
305            Ok(layout) => layout,
306            Err(_) => {
307                return Err(self.err(ReflectErrorKind::Unsized {
308                    shape: value_shape,
309                    operation: "begin_value allocating value",
310                }));
311            }
312        };
313        let value_ptr = facet_core::alloc_for_layout(value_layout);
314
315        // Store the value pointer in the insert state and mark as building value
316        match &mut frame.tracker {
317            Tracker::Map {
318                insert_state,
319                building_key,
320                ..
321            } => {
322                *building_key = false; // Now building value, not key
323                *insert_state = MapInsertState::PushingValue {
324                    key_ptr,
325                    value_ptr: Some(value_ptr),
326                    value_initialized: false,
327                    value_frame_on_stack: true, // TrackedBuffer frame is now on the stack
328                    key_frame_stored,           // Preserve from previous state
329                };
330            }
331            _ => unreachable!(),
332        }
333
334        // Push a new frame for the value
335        // Get child type plan NodeId for map values
336        let child_plan_id = self
337            .root_plan
338            .map_value_node_id(parent_type_plan)
339            .expect("TypePlan must have map value node");
340        self.mode.stack_mut().push(Frame::new(
341            value_ptr,
342            AllocatedShape::new(value_shape, value_layout.size()),
343            FrameOwnership::TrackedBuffer,
344            child_plan_id,
345        ));
346
347        Ok(self)
348    }
349
350    /// Begins an object entry for a DynamicValue object.
351    ///
352    /// This is a simpler API than begin_key/begin_value for DynamicValue objects,
353    /// where keys are always strings. The key is stored and a frame is pushed for
354    /// the value. After setting the value and calling `end()`, the key-value pair
355    /// will be inserted into the object.
356    ///
357    /// For `Def::Map` types, use `begin_key()` / `begin_value()` instead.
358    pub fn begin_object_entry(mut self, key: &str) -> Result<Self, ReflectError> {
359        crate::trace!("begin_object_entry({key:?})");
360
361        // Get shape and type_plan upfront to avoid borrow conflicts
362        let frame = self.frames().last().unwrap();
363        let shape = frame.allocated.shape();
364        let parent_type_plan = frame.type_plan;
365        let frame = self.mode.stack_mut().last_mut().unwrap();
366
367        // Check that we have a DynamicValue in Object state with Idle insert_state
368        let dyn_def = match (&shape.def, &frame.tracker) {
369            (
370                Def::DynamicValue(dyn_def),
371                Tracker::DynamicValue {
372                    state:
373                        DynamicValueState::Object {
374                            insert_state: DynamicObjectInsertState::Idle,
375                            ..
376                        },
377                },
378            ) if frame.is_init => {
379                // Good, proceed
380                dyn_def
381            }
382            (
383                Def::DynamicValue(_),
384                Tracker::DynamicValue {
385                    state:
386                        DynamicValueState::Object {
387                            insert_state: DynamicObjectInsertState::BuildingValue { .. },
388                            ..
389                        },
390                },
391            ) => {
392                return Err(self.err(ReflectErrorKind::OperationFailed {
393                    shape,
394                    operation: "already building a value, call end() first",
395                }));
396            }
397            (Def::DynamicValue(_), _) => {
398                return Err(self.err(ReflectErrorKind::OperationFailed {
399                    shape,
400                    operation: "must call init_map() before begin_object_entry()",
401                }));
402            }
403            _ => {
404                return Err(self.err(ReflectErrorKind::OperationFailed {
405                    shape,
406                    operation: "begin_object_entry can only be called on DynamicValue types",
407                }));
408            }
409        };
410
411        // For DynamicValue objects, the value shape is the same DynamicValue shape
412        let value_shape = shape;
413
414        // In deferred mode, check if the key exists in pending_entries first.
415        // This is needed for TOML array-of-tables: [[a.b]] adds to the same array incrementally.
416        // Each [[a.b]] section ends the array temporarily, but subsequent sections should
417        // re-enter and append to the same array, not create a new one.
418        if let Tracker::DynamicValue {
419            state: DynamicValueState::Object {
420                pending_entries, ..
421            },
422        } = &frame.tracker
423            && let Some(idx) = pending_entries.iter().position(|(k, _)| k == key)
424        {
425            let value_ptr = pending_entries[idx].1;
426            let value_size = value_shape
427                .layout
428                .sized_layout()
429                .expect("value must be sized")
430                .size();
431            let child_plan = parent_type_plan;
432            let mut new_frame = Frame::new(
433                value_ptr,
434                AllocatedShape::new(value_shape, value_size),
435                FrameOwnership::BorrowedInPlace,
436                child_plan,
437            );
438            new_frame.is_init = true;
439            // For DynamicValue, we need to check the actual value to set the right tracker.
440            // The value is already initialized, so we set Scalar and let subsequent
441            // operations (init_list, init_map) handle the conversion appropriately.
442            new_frame.tracker = Tracker::Scalar;
443            crate::trace!("begin_object_entry({key:?}): re-entering pending entry at index {idx}");
444            self.mode.stack_mut().push(new_frame);
445            return Ok(self);
446        }
447
448        // Check if key already exists using object_get_mut (for "get or create" semantics)
449        // This is needed for formats like TOML with implicit tables: [a] followed by [a.b.c]
450        if let Some(get_mut_fn) = dyn_def.vtable.object_get_mut {
451            let object_ptr = unsafe { frame.data.assume_init() };
452            if let Some(existing_ptr) = unsafe { get_mut_fn(object_ptr, key) } {
453                // Key exists - push a frame pointing to existing value
454                // Leave insert_state as Idle (no insertion needed on end())
455                // Use ManagedElsewhere since parent object owns this value
456                let value_size = value_shape
457                    .layout
458                    .sized_layout()
459                    .expect("value must be sized")
460                    .size();
461                // For DynamicValue, use the same type plan (self-recursive)
462                let child_plan = parent_type_plan;
463                let mut new_frame = Frame::new(
464                    existing_ptr.as_uninit(),
465                    AllocatedShape::new(value_shape, value_size),
466                    FrameOwnership::BorrowedInPlace,
467                    child_plan,
468                );
469                new_frame.is_init = true;
470                // Set tracker to reflect it's an initialized DynamicValue
471                // For DynamicValue, we need to peek at the value to determine the state.
472                // However, we don't know yet what operations will be called (init_map, init_list, etc.)
473                // So we set Scalar tracker and let init_map/init_list handle the conversion.
474                // init_list will convert Scalar->List if shape is Def::List, or handle DynamicValue directly.
475                new_frame.tracker = Tracker::Scalar;
476                self.mode.stack_mut().push(new_frame);
477                return Ok(self);
478            }
479        }
480
481        // Key doesn't exist - allocate new value
482        let value_layout = match value_shape.layout.sized_layout() {
483            Ok(layout) => layout,
484            Err(_) => {
485                return Err(self.err(ReflectErrorKind::Unsized {
486                    shape: value_shape,
487                    operation: "begin_object_entry: calculating value layout",
488                }));
489            }
490        };
491
492        let value_ptr = facet_core::alloc_for_layout(value_layout);
493
494        // Update the insert state with the key
495        match &mut frame.tracker {
496            Tracker::DynamicValue {
497                state: DynamicValueState::Object { insert_state, .. },
498            } => {
499                *insert_state = DynamicObjectInsertState::BuildingValue {
500                    key: String::from(key),
501                };
502            }
503            _ => unreachable!(),
504        }
505
506        // Push a new frame for the value
507        // For DynamicValue, use the same type plan (self-recursive)
508        let child_plan = parent_type_plan;
509        self.mode.stack_mut().push(Frame::new(
510            value_ptr,
511            AllocatedShape::new(value_shape, value_layout.size()),
512            FrameOwnership::Owned,
513            child_plan,
514        ));
515
516        Ok(self)
517    }
518}