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