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}