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