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}