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        fr.deinit();
75
76        // SAFETY: `fr.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.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        fr.deinit();
107
108        // If source shape is also the same DynamicValue shape, just copy it
109        if fr.shape.is_shape(src_shape) {
110            unsafe {
111                fr.data.copy_from(src_value, fr.shape).unwrap();
112                fr.mark_as_init();
113            }
114            return Ok(self);
115        }
116
117        // Get the size in bits for numeric conversions
118        let size_bits = src_shape
119            .layout
120            .sized_layout()
121            .map(|l| l.size() * 8)
122            .unwrap_or(0);
123
124        // Convert based on source shape's type
125        match &src_shape.ty {
126            Type::Primitive(PrimitiveType::Boolean) => {
127                let val = unsafe { *(src_value.as_byte_ptr() as *const bool) };
128                unsafe { (vtable.set_bool)(fr.data, val) };
129            }
130            Type::Primitive(PrimitiveType::Numeric(NumericType::Float)) => {
131                if size_bits == 64 {
132                    let val = unsafe { *(src_value.as_byte_ptr() as *const f64) };
133                    let success = unsafe { (vtable.set_f64)(fr.data, val) };
134                    if !success {
135                        return Err(ReflectError::OperationFailed {
136                            shape: src_shape,
137                            operation: "f64 value (NaN/Infinity) not representable in dynamic value",
138                        });
139                    }
140                } else if size_bits == 32 {
141                    let val = unsafe { *(src_value.as_byte_ptr() as *const f32) } as f64;
142                    let success = unsafe { (vtable.set_f64)(fr.data, val) };
143                    if !success {
144                        return Err(ReflectError::OperationFailed {
145                            shape: src_shape,
146                            operation: "f32 value (NaN/Infinity) not representable in dynamic value",
147                        });
148                    }
149                } else {
150                    return Err(ReflectError::OperationFailed {
151                        shape: src_shape,
152                        operation: "unsupported float size for dynamic value",
153                    });
154                }
155            }
156            Type::Primitive(PrimitiveType::Numeric(NumericType::Integer { signed: true })) => {
157                let val: i64 = match size_bits {
158                    8 => (unsafe { *(src_value.as_byte_ptr() as *const i8) }) as i64,
159                    16 => (unsafe { *(src_value.as_byte_ptr() as *const i16) }) as i64,
160                    32 => (unsafe { *(src_value.as_byte_ptr() as *const i32) }) as i64,
161                    64 => unsafe { *(src_value.as_byte_ptr() as *const i64) },
162                    _ => {
163                        return Err(ReflectError::OperationFailed {
164                            shape: src_shape,
165                            operation: "unsupported signed integer size for dynamic value",
166                        });
167                    }
168                };
169                unsafe { (vtable.set_i64)(fr.data, val) };
170            }
171            Type::Primitive(PrimitiveType::Numeric(NumericType::Integer { signed: false })) => {
172                let val: u64 = match size_bits {
173                    8 => (unsafe { *src_value.as_byte_ptr() }) as u64,
174                    16 => (unsafe { *(src_value.as_byte_ptr() as *const u16) }) as u64,
175                    32 => (unsafe { *(src_value.as_byte_ptr() as *const u32) }) as u64,
176                    64 => unsafe { *(src_value.as_byte_ptr() as *const u64) },
177                    _ => {
178                        return Err(ReflectError::OperationFailed {
179                            shape: src_shape,
180                            operation: "unsupported unsigned integer size for dynamic value",
181                        });
182                    }
183                };
184                unsafe { (vtable.set_u64)(fr.data, val) };
185            }
186            Type::Primitive(PrimitiveType::Textual(_)) => {
187                // char or str - for char, convert to string
188                if src_shape.type_identifier == "char" {
189                    let c = unsafe { *(src_value.as_byte_ptr() as *const char) };
190                    let mut buf = [0u8; 4];
191                    let s = c.encode_utf8(&mut buf);
192                    unsafe { (vtable.set_str)(fr.data, s) };
193                } else {
194                    // &str
195                    let s: &str = unsafe { *(src_value.as_byte_ptr() as *const &str) };
196                    unsafe { (vtable.set_str)(fr.data, s) };
197                }
198            }
199            _ => {
200                // Handle String type (not a primitive but common)
201                if src_shape.type_identifier == "String" {
202                    let s: &::alloc::string::String =
203                        unsafe { &*(src_value.as_byte_ptr() as *const ::alloc::string::String) };
204                    unsafe { (vtable.set_str)(fr.data, s.as_str()) };
205                    // Drop the source String since we cloned its content
206                    unsafe {
207                        src_shape
208                            .call_drop_in_place(PtrMut::new(src_value.as_byte_ptr() as *mut u8));
209                    }
210                } else {
211                    return Err(ReflectError::OperationFailed {
212                        shape: src_shape,
213                        operation: "cannot convert this type to dynamic value",
214                    });
215                }
216            }
217        }
218
219        let fr = self.frames_mut().last_mut().unwrap();
220        fr.tracker = Tracker::DynamicValue {
221            state: DynamicValueState::Scalar,
222        };
223        unsafe { fr.mark_as_init() };
224        Ok(self)
225    }
226
227    /// Sets a datetime value into a DynamicValue target.
228    ///
229    /// This is used for format-specific datetime types (like TOML datetime).
230    /// Returns an error if the target doesn't support datetime values.
231    #[allow(clippy::too_many_arguments)]
232    pub fn set_datetime(
233        mut self,
234        year: i32,
235        month: u8,
236        day: u8,
237        hour: u8,
238        minute: u8,
239        second: u8,
240        nanos: u32,
241        kind: DynDateTimeKind,
242    ) -> Result<Self, ReflectError> {
243        let fr = self.frames_mut().last_mut().unwrap();
244
245        // Must be a DynamicValue type
246        let dyn_def = match &fr.shape.def {
247            Def::DynamicValue(dv) => dv,
248            _ => {
249                return Err(ReflectError::OperationFailed {
250                    shape: fr.shape,
251                    operation: "set_datetime requires a DynamicValue target",
252                });
253            }
254        };
255
256        let vtable = dyn_def.vtable;
257
258        // Check if the vtable supports datetime
259        let Some(set_datetime_fn) = vtable.set_datetime else {
260            return Err(ReflectError::OperationFailed {
261                shape: fr.shape,
262                operation: "dynamic value type does not support datetime",
263            });
264        };
265
266        fr.deinit();
267
268        // Call the vtable's set_datetime function
269        unsafe {
270            set_datetime_fn(fr.data, year, month, day, hour, minute, second, nanos, kind);
271        }
272
273        let fr = self.frames_mut().last_mut().unwrap();
274        fr.tracker = Tracker::DynamicValue {
275            state: DynamicValueState::Scalar,
276        };
277        unsafe { fr.mark_as_init() };
278        Ok(self)
279    }
280
281    /// Sets the current frame using a function that initializes the value
282    ///
283    /// # Safety
284    ///
285    /// If `f` returns Ok(), it is assumed that it initialized the passed pointer fully and with a
286    /// value of the right type.
287    ///
288    /// If `f` returns Err(), it is assumed that it did NOT initialize the passed pointer and that
289    /// there is no need to drop it in place.
290    pub unsafe fn set_from_function<F>(mut self, f: F) -> Result<Self, ReflectError>
291    where
292        F: FnOnce(PtrUninit) -> Result<(), ReflectError>,
293    {
294        let frame = self.frames_mut().last_mut().unwrap();
295
296        frame.deinit();
297        f(frame.data)?;
298
299        // safety: `f()` returned Ok, so `frame.data` must be initialized
300        unsafe {
301            frame.mark_as_init();
302        }
303
304        Ok(self)
305    }
306
307    /// Sets the current frame to its default value using `default_in_place` from the
308    /// vtable.
309    ///
310    /// Note: if you have `struct S { field: F }`, and `F` does not implement `Default`
311    /// but `S` does, this doesn't magically uses S's `Default` implementation to get a value
312    /// for `field`.
313    ///
314    /// If the current frame's shape does not implement `Default`, then this returns an error.
315    #[inline]
316    pub fn set_default(self) -> Result<Self, ReflectError> {
317        let frame = self.frames().last().unwrap();
318        let shape = frame.shape;
319
320        // SAFETY: `call_default_in_place` fully initializes the passed pointer.
321        unsafe {
322            self.set_from_function(move |ptr| {
323                shape.call_default_in_place(ptr.assume_init()).ok_or(
324                    ReflectError::OperationFailed {
325                        shape,
326                        operation: "type does not implement Default",
327                    },
328                )?;
329                Ok(())
330            })
331        }
332    }
333
334    /// Copy a value from a Peek into the current frame.
335    ///
336    /// # Invariants
337    ///
338    /// `peek` must be a thin pointer, otherwise this panics.
339    ///
340    /// # Safety
341    ///
342    /// If this succeeds, the value `Peek` points to has been moved out of, and
343    /// as such, should not be dropped (but should be deallocated).
344    pub unsafe fn set_from_peek(self, peek: &Peek<'_, '_>) -> Result<Self, ReflectError> {
345        // Get the source value's pointer and shape
346        let src_ptr = peek.data();
347        let src_shape = peek.shape();
348
349        // SAFETY: `Peek` guarantees that src_ptr is initialized and of type src_shape
350        unsafe { self.set_shape(src_ptr, src_shape) }
351    }
352
353    /// Parses a string value into the current frame using the type's ParseFn from the vtable.
354    ///
355    /// If the current frame was previously initialized, its contents are dropped in place.
356    pub fn parse_from_str(mut self, s: &str) -> Result<Self, ReflectError> {
357        let frame = self.frames_mut().last_mut().unwrap();
358        let shape = frame.shape;
359
360        // Note: deinit leaves us in `Tracker::Uninit` state which is valid even if we error out.
361        frame.deinit();
362
363        // Parse the string value using the type's parse function
364        let result = unsafe { shape.call_parse(s, frame.data.assume_init()) };
365
366        match result {
367            Some(Ok(())) => {
368                // SAFETY: `call_parse` returned `Ok`, so `frame.data` is fully initialized now.
369                unsafe {
370                    frame.mark_as_init();
371                }
372                Ok(self)
373            }
374            Some(Err(_pe)) => {
375                // TODO: can we propagate the ParseError somehow?
376                Err(ReflectError::OperationFailed {
377                    shape,
378                    operation: "Failed to parse string value",
379                })
380            }
381            None => Err(ReflectError::OperationFailed {
382                shape,
383                operation: "Type does not support parsing from string",
384            }),
385        }
386    }
387}