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}