facet_reflect/partial/partial_api/
set.rs

1use super::*;
2use facet_core::{Def, DynDateTimeKind, NumericType, PrimitiveType, Type};
3
4////////////////////////////////////////////////////////////////////////////////////////////////////
5// `Set` and set helpers
6////////////////////////////////////////////////////////////////////////////////////////////////////
7impl<'facet, const BORROW: bool> Partial<'facet, BORROW> {
8    /// Sets a value wholesale into the current frame.
9    ///
10    /// If the current frame was already initialized, the previous value is
11    /// dropped. If it was partially initialized, the fields that were initialized
12    /// are dropped, etc.
13    pub fn set<U>(mut self, value: U) -> Result<Self, ReflectError>
14    where
15        U: Facet<'facet>,
16    {
17        struct DropVal<U> {
18            ptr: *mut U,
19        }
20        impl<U> Drop for DropVal<U> {
21            #[inline]
22            fn drop(&mut self) {
23                unsafe { core::ptr::drop_in_place(self.ptr) };
24            }
25        }
26
27        let mut value = ManuallyDrop::new(value);
28        let drop = DropVal {
29            ptr: (&mut value) as *mut ManuallyDrop<U> as *mut U,
30        };
31
32        let ptr_const = PtrConst::new(drop.ptr);
33        // Safety: We are calling set_shape with a valid shape and a valid pointer
34        self = unsafe { self.set_shape(ptr_const, U::SHAPE)? };
35        core::mem::forget(drop);
36
37        Ok(self)
38    }
39
40    /// Sets a value into the current frame by [PtrConst] / [Shape].
41    ///
42    /// # Safety
43    ///
44    /// The caller must ensure that `src_value` points to a valid instance of a value
45    /// whose memory layout and type matches `src_shape`, and that this value can be
46    /// safely copied (bitwise) into the destination specified by the Partial's current frame.
47    ///
48    /// After a successful call, the ownership of the value at `src_value` is effectively moved
49    /// into the Partial (i.e., the destination), and the original value should not be used
50    /// or dropped by the caller; you should use `core::mem::forget` on the passed value.
51    ///
52    /// If an error is returned, the destination remains unmodified and safe for future operations.
53    #[inline]
54    pub unsafe fn set_shape(
55        mut self,
56        src_value: PtrConst,
57        src_shape: &'static Shape,
58    ) -> Result<Self, ReflectError> {
59        let fr = self.frames_mut().last_mut().unwrap();
60        crate::trace!("set_shape({src_shape:?})");
61
62        // Check if target is a DynamicValue - if so, convert the source value
63        if let Def::DynamicValue(dyn_def) = &fr.shape.def {
64            return unsafe { self.set_into_dynamic_value(src_value, src_shape, dyn_def) };
65        }
66
67        if !fr.shape.is_shape(src_shape) {
68            return Err(ReflectError::WrongShape {
69                expected: fr.shape,
70                actual: src_shape,
71            });
72        }
73
74        // Special case: if this is a ManagedElsewhere frame and it's initialized,
75        // we need to drop the old value before replacing it (same reason as in set_into_dynamic_value)
76        if matches!(fr.ownership, FrameOwnership::ManagedElsewhere) && fr.is_init {
77            unsafe { fr.shape.call_drop_in_place(fr.data.assume_init()) };
78        }
79
80        fr.deinit();
81
82        // SAFETY: `fr.shape` and `src_shape` are the same, so they have the same size,
83        // and the preconditions for this function are that `src_value` is fully intialized.
84        unsafe {
85            // unwrap safety: the only failure condition for copy_from is that shape is unsized,
86            // which is not possible for `Partial`
87            fr.data.copy_from(src_value, fr.shape).unwrap();
88        }
89
90        // SAFETY: if we reached this point, `fr.data` is correctly initialized
91        unsafe {
92            fr.mark_as_init();
93        }
94
95        Ok(self)
96    }
97
98    /// Sets a value into a DynamicValue target by converting the source value.
99    ///
100    /// # Safety
101    ///
102    /// Same safety requirements as `set_shape`.
103    unsafe fn set_into_dynamic_value(
104        mut self,
105        src_value: PtrConst,
106        src_shape: &'static Shape,
107        dyn_def: &facet_core::DynamicValueDef,
108    ) -> Result<Self, ReflectError> {
109        let fr = self.frames_mut().last_mut().unwrap();
110        let vtable = dyn_def.vtable;
111
112        // Special case: if this is a ManagedElsewhere frame (pointing into parent object)
113        // and it's initialized, we need to drop the old value before replacing it.
114        // deinit() normally skips dropping ManagedElsewhere to avoid double-free,
115        // but when we're explicitly replacing via set(), we own that operation.
116        if matches!(fr.ownership, FrameOwnership::ManagedElsewhere) && fr.is_init {
117            unsafe { fr.shape.call_drop_in_place(fr.data.assume_init()) };
118        }
119
120        fr.deinit();
121
122        // If source shape is also the same DynamicValue shape, just copy it
123        if fr.shape.is_shape(src_shape) {
124            unsafe {
125                fr.data.copy_from(src_value, fr.shape).unwrap();
126                fr.mark_as_init();
127            }
128            return Ok(self);
129        }
130
131        // Get the size in bits for numeric conversions
132        let size_bits = src_shape
133            .layout
134            .sized_layout()
135            .map(|l| l.size() * 8)
136            .unwrap_or(0);
137
138        // Convert based on source shape's type
139        match &src_shape.ty {
140            Type::Primitive(PrimitiveType::Boolean) => {
141                let val = unsafe { *(src_value.as_byte_ptr() as *const bool) };
142                unsafe { (vtable.set_bool)(fr.data, val) };
143            }
144            Type::Primitive(PrimitiveType::Numeric(NumericType::Float)) => {
145                if size_bits == 64 {
146                    let val = unsafe { *(src_value.as_byte_ptr() as *const f64) };
147                    let success = unsafe { (vtable.set_f64)(fr.data, val) };
148                    if !success {
149                        return Err(ReflectError::OperationFailed {
150                            shape: src_shape,
151                            operation: "f64 value (NaN/Infinity) not representable in dynamic value",
152                        });
153                    }
154                } else if size_bits == 32 {
155                    let val = unsafe { *(src_value.as_byte_ptr() as *const f32) } as f64;
156                    let success = unsafe { (vtable.set_f64)(fr.data, val) };
157                    if !success {
158                        return Err(ReflectError::OperationFailed {
159                            shape: src_shape,
160                            operation: "f32 value (NaN/Infinity) not representable in dynamic value",
161                        });
162                    }
163                } else {
164                    return Err(ReflectError::OperationFailed {
165                        shape: src_shape,
166                        operation: "unsupported float size for dynamic value",
167                    });
168                }
169            }
170            Type::Primitive(PrimitiveType::Numeric(NumericType::Integer { signed: true })) => {
171                let val: i64 = match size_bits {
172                    8 => (unsafe { *(src_value.as_byte_ptr() as *const i8) }) as i64,
173                    16 => (unsafe { *(src_value.as_byte_ptr() as *const i16) }) as i64,
174                    32 => (unsafe { *(src_value.as_byte_ptr() as *const i32) }) as i64,
175                    64 => unsafe { *(src_value.as_byte_ptr() as *const i64) },
176                    _ => {
177                        return Err(ReflectError::OperationFailed {
178                            shape: src_shape,
179                            operation: "unsupported signed integer size for dynamic value",
180                        });
181                    }
182                };
183                unsafe { (vtable.set_i64)(fr.data, val) };
184            }
185            Type::Primitive(PrimitiveType::Numeric(NumericType::Integer { signed: false })) => {
186                let val: u64 = match size_bits {
187                    8 => (unsafe { *src_value.as_byte_ptr() }) as u64,
188                    16 => (unsafe { *(src_value.as_byte_ptr() as *const u16) }) as u64,
189                    32 => (unsafe { *(src_value.as_byte_ptr() as *const u32) }) as u64,
190                    64 => unsafe { *(src_value.as_byte_ptr() as *const u64) },
191                    _ => {
192                        return Err(ReflectError::OperationFailed {
193                            shape: src_shape,
194                            operation: "unsupported unsigned integer size for dynamic value",
195                        });
196                    }
197                };
198                unsafe { (vtable.set_u64)(fr.data, val) };
199            }
200            Type::Primitive(PrimitiveType::Textual(_)) => {
201                // char or str - for char, convert to string
202                if src_shape.type_identifier == "char" {
203                    let c = unsafe { *(src_value.as_byte_ptr() as *const char) };
204                    let mut buf = [0u8; 4];
205                    let s = c.encode_utf8(&mut buf);
206                    unsafe { (vtable.set_str)(fr.data, s) };
207                } else {
208                    // &str
209                    let s: &str = unsafe { *(src_value.as_byte_ptr() as *const &str) };
210                    unsafe { (vtable.set_str)(fr.data, s) };
211                }
212            }
213            _ => {
214                // Handle String type (not a primitive but common)
215                if src_shape.type_identifier == "String" {
216                    let s: &::alloc::string::String =
217                        unsafe { &*(src_value.as_byte_ptr() as *const ::alloc::string::String) };
218                    unsafe { (vtable.set_str)(fr.data, s.as_str()) };
219                    // Drop the source String since we cloned its content
220                    unsafe {
221                        src_shape
222                            .call_drop_in_place(PtrMut::new(src_value.as_byte_ptr() as *mut u8));
223                    }
224                } else {
225                    return Err(ReflectError::OperationFailed {
226                        shape: src_shape,
227                        operation: "cannot convert this type to dynamic value",
228                    });
229                }
230            }
231        }
232
233        let fr = self.frames_mut().last_mut().unwrap();
234        fr.tracker = Tracker::DynamicValue {
235            state: DynamicValueState::Scalar,
236        };
237        unsafe { fr.mark_as_init() };
238        Ok(self)
239    }
240
241    /// Sets a datetime value into a DynamicValue target.
242    ///
243    /// This is used for format-specific datetime types (like TOML datetime).
244    /// Returns an error if the target doesn't support datetime values.
245    #[allow(clippy::too_many_arguments)]
246    pub fn set_datetime(
247        mut self,
248        year: i32,
249        month: u8,
250        day: u8,
251        hour: u8,
252        minute: u8,
253        second: u8,
254        nanos: u32,
255        kind: DynDateTimeKind,
256    ) -> Result<Self, ReflectError> {
257        let fr = self.frames_mut().last_mut().unwrap();
258
259        // Must be a DynamicValue type
260        let dyn_def = match &fr.shape.def {
261            Def::DynamicValue(dv) => dv,
262            _ => {
263                return Err(ReflectError::OperationFailed {
264                    shape: fr.shape,
265                    operation: "set_datetime requires a DynamicValue target",
266                });
267            }
268        };
269
270        let vtable = dyn_def.vtable;
271
272        // Check if the vtable supports datetime
273        let Some(set_datetime_fn) = vtable.set_datetime else {
274            return Err(ReflectError::OperationFailed {
275                shape: fr.shape,
276                operation: "dynamic value type does not support datetime",
277            });
278        };
279
280        fr.deinit();
281
282        // Call the vtable's set_datetime function
283        unsafe {
284            set_datetime_fn(fr.data, year, month, day, hour, minute, second, nanos, kind);
285        }
286
287        let fr = self.frames_mut().last_mut().unwrap();
288        fr.tracker = Tracker::DynamicValue {
289            state: DynamicValueState::Scalar,
290        };
291        unsafe { fr.mark_as_init() };
292        Ok(self)
293    }
294
295    /// Sets the current frame using a function that initializes the value
296    ///
297    /// # Safety
298    ///
299    /// If `f` returns Ok(), it is assumed that it initialized the passed pointer fully and with a
300    /// value of the right type.
301    ///
302    /// If `f` returns Err(), it is assumed that it did NOT initialize the passed pointer and that
303    /// there is no need to drop it in place.
304    pub unsafe fn set_from_function<F>(mut self, f: F) -> Result<Self, ReflectError>
305    where
306        F: FnOnce(PtrUninit) -> Result<(), ReflectError>,
307    {
308        let frame = self.frames_mut().last_mut().unwrap();
309
310        // Special case: if this is a ManagedElsewhere frame and it's initialized,
311        // we need to drop the old value before replacing it.
312        // deinit() normally skips dropping ManagedElsewhere to avoid double-free,
313        // but when we're explicitly replacing via set_from_function(), we own that operation.
314        if matches!(frame.ownership, FrameOwnership::ManagedElsewhere) && frame.is_init {
315            unsafe { frame.shape.call_drop_in_place(frame.data.assume_init()) };
316        }
317
318        frame.deinit();
319        f(frame.data)?;
320
321        // safety: `f()` returned Ok, so `frame.data` must be initialized
322        unsafe {
323            frame.mark_as_init();
324        }
325
326        Ok(self)
327    }
328
329    /// Sets the current frame to its default value using `default_in_place` from the
330    /// vtable.
331    ///
332    /// Note: if you have `struct S { field: F }`, and `F` does not implement `Default`
333    /// but `S` does, this doesn't magically uses S's `Default` implementation to get a value
334    /// for `field`.
335    ///
336    /// If the current frame's shape does not implement `Default`, then this returns an error.
337    #[inline]
338    pub fn set_default(self) -> Result<Self, ReflectError> {
339        let frame = self.frames().last().unwrap();
340        let shape = frame.shape;
341
342        // SAFETY: `call_default_in_place` fully initializes the passed pointer.
343        unsafe {
344            self.set_from_function(move |ptr| {
345                shape.call_default_in_place(ptr.assume_init()).ok_or(
346                    ReflectError::OperationFailed {
347                        shape,
348                        operation: "type does not implement Default",
349                    },
350                )?;
351                Ok(())
352            })
353        }
354    }
355
356    /// Copy a value from a Peek into the current frame.
357    ///
358    /// # Invariants
359    ///
360    /// `peek` must be a thin pointer, otherwise this panics.
361    ///
362    /// # Safety
363    ///
364    /// If this succeeds, the value `Peek` points to has been moved out of, and
365    /// as such, should not be dropped (but should be deallocated).
366    pub unsafe fn set_from_peek(self, peek: &Peek<'_, '_>) -> Result<Self, ReflectError> {
367        // Get the source value's pointer and shape
368        let src_ptr = peek.data();
369        let src_shape = peek.shape();
370
371        // SAFETY: `Peek` guarantees that src_ptr is initialized and of type src_shape
372        unsafe { self.set_shape(src_ptr, src_shape) }
373    }
374
375    /// Parses a string value into the current frame using the type's ParseFn from the vtable.
376    ///
377    /// If the current frame was previously initialized, its contents are dropped in place.
378    pub fn parse_from_str(mut self, s: &str) -> Result<Self, ReflectError> {
379        let frame = self.frames_mut().last_mut().unwrap();
380        let shape = frame.shape;
381
382        // Note: deinit leaves us in `Tracker::Uninit` state which is valid even if we error out.
383        frame.deinit();
384
385        // Parse the string value using the type's parse function
386        let result = unsafe { shape.call_parse(s, frame.data.assume_init()) };
387
388        match result {
389            Some(Ok(())) => {
390                // SAFETY: `call_parse` returned `Ok`, so `frame.data` is fully initialized now.
391                unsafe {
392                    frame.mark_as_init();
393                }
394                Ok(self)
395            }
396            Some(Err(_pe)) => {
397                // TODO: can we propagate the ParseError somehow?
398                Err(ReflectError::OperationFailed {
399                    shape,
400                    operation: "Failed to parse string value",
401                })
402            }
403            None => Err(ReflectError::OperationFailed {
404                shape,
405                operation: "Type does not support parsing from string",
406            }),
407        }
408    }
409
410    /// Parses a byte slice into the current frame using the type's ParseBytesFn from the vtable.
411    ///
412    /// This is used for binary formats where types have efficient binary representations
413    /// (e.g., UUID as 16 raw bytes instead of a string).
414    ///
415    /// If the current frame was previously initialized, its contents are dropped in place.
416    pub fn parse_from_bytes(mut self, bytes: &[u8]) -> Result<Self, ReflectError> {
417        let frame = self.frames_mut().last_mut().unwrap();
418        let shape = frame.shape;
419
420        // Note: deinit leaves us in `Tracker::Uninit` state which is valid even if we error out.
421        frame.deinit();
422
423        // Parse the bytes using the type's parse_bytes function
424        let result = unsafe { shape.call_parse_bytes(bytes, frame.data.assume_init()) };
425
426        match result {
427            Some(Ok(())) => {
428                // SAFETY: `call_parse_bytes` returned `Ok`, so `frame.data` is fully initialized.
429                unsafe {
430                    frame.mark_as_init();
431                }
432                Ok(self)
433            }
434            Some(Err(_pe)) => {
435                // TODO: can we propagate the ParseError somehow?
436                Err(ReflectError::OperationFailed {
437                    shape,
438                    operation: "Failed to parse bytes value",
439                })
440            }
441            None => Err(ReflectError::OperationFailed {
442                shape,
443                operation: "Type does not support parsing from bytes",
444            }),
445        }
446    }
447}