facet_reflect/poke/
value.rs

1use core::marker::PhantomData;
2
3use facet_core::{Def, Facet, PtrConst, PtrMut, Shape, Type, UserType};
4
5use crate::ReflectError;
6
7use super::PokeStruct;
8
9/// A mutable view into a value with runtime type information.
10///
11/// `Poke` provides reflection capabilities for mutating values at runtime.
12/// It is the mutable counterpart to [`Peek`](crate::Peek).
13///
14/// # Wholesale Replacement vs Field Mutation
15///
16/// `Poke` can be created for any type. Replacing a value wholesale with [`Poke::set`]
17/// is always safe - it just drops the old value and writes the new one.
18///
19/// However, mutating individual struct fields via [`PokeStruct::set_field`] requires
20/// the struct to be marked as POD (`#[facet(pod)]`). This is because field mutation
21/// could violate struct-level invariants.
22///
23/// # Lifetime Parameters
24///
25/// - `'mem`: The memory lifetime - how long the underlying data is valid
26/// - `'facet`: The type's lifetime parameter (for types like `&'a str`)
27///
28/// # Example
29///
30/// ```ignore
31/// // Wholesale replacement works on any type
32/// let mut s = String::from("hello");
33/// let mut poke = Poke::new(&mut s);
34/// poke.set(String::from("world")).unwrap();
35///
36/// // Field mutation requires #[facet(pod)]
37/// #[derive(Facet)]
38/// #[facet(pod)]
39/// struct Point { x: i32, y: i32 }
40///
41/// let mut point = Point { x: 1, y: 2 };
42/// let mut poke = Poke::new(&mut point);
43/// poke.into_struct().unwrap().set_field_by_name("x", 10i32).unwrap();
44/// assert_eq!(point.x, 10);
45/// ```
46pub struct Poke<'mem, 'facet> {
47    /// Underlying data (mutable)
48    pub(crate) data: PtrMut,
49
50    /// Shape of the value
51    pub(crate) shape: &'static Shape,
52
53    /// Invariant over 'facet (same reasoning as Peek)
54    /// Covariant over 'mem but with mutable access
55    #[allow(clippy::type_complexity)]
56    _marker: PhantomData<(&'mem mut (), fn(&'facet ()) -> &'facet ())>,
57}
58
59impl<'mem, 'facet> Poke<'mem, 'facet> {
60    /// Creates a mutable view over a `T` value.
61    ///
62    /// This always succeeds - wholesale replacement via [`Poke::set`] is safe for any type.
63    /// The POD check happens when you try to mutate individual struct fields.
64    pub fn new<T: Facet<'facet>>(t: &'mem mut T) -> Self {
65        Self {
66            data: PtrMut::new(t as *mut T as *mut u8),
67            shape: T::SHAPE,
68            _marker: PhantomData,
69        }
70    }
71
72    /// Creates a mutable view from raw parts without any validation.
73    ///
74    /// # Safety
75    ///
76    /// - `data` must point to a valid, initialized value of the type described by `shape`
77    /// - `data` must be valid for the lifetime `'mem`
78    pub unsafe fn from_raw_parts(data: PtrMut, shape: &'static Shape) -> Self {
79        Self {
80            data,
81            shape,
82            _marker: PhantomData,
83        }
84    }
85
86    /// Returns the shape of the value.
87    #[inline(always)]
88    pub fn shape(&self) -> &'static Shape {
89        self.shape
90    }
91
92    /// Returns a const pointer to the underlying data.
93    #[inline(always)]
94    pub fn data(&self) -> PtrConst {
95        self.data.as_const()
96    }
97
98    /// Returns a mutable pointer to the underlying data.
99    #[inline(always)]
100    pub fn data_mut(&mut self) -> PtrMut {
101        self.data
102    }
103
104    /// Returns true if this value is a struct.
105    #[inline]
106    pub fn is_struct(&self) -> bool {
107        matches!(self.shape.ty, Type::User(UserType::Struct(_)))
108    }
109
110    /// Returns true if this value is an enum.
111    #[inline]
112    pub fn is_enum(&self) -> bool {
113        matches!(self.shape.ty, Type::User(UserType::Enum(_)))
114    }
115
116    /// Returns true if this value is a scalar (primitive type).
117    #[inline]
118    pub fn is_scalar(&self) -> bool {
119        matches!(self.shape.def, Def::Scalar)
120    }
121
122    /// Converts this into a `PokeStruct` if the value is a struct.
123    pub fn into_struct(self) -> Result<PokeStruct<'mem, 'facet>, ReflectError> {
124        match self.shape.ty {
125            Type::User(UserType::Struct(struct_type)) => Ok(PokeStruct {
126                value: self,
127                ty: struct_type,
128            }),
129            _ => Err(ReflectError::WasNotA {
130                expected: "struct",
131                actual: self.shape,
132            }),
133        }
134    }
135
136    /// Converts this into a `PokeEnum` if the value is an enum.
137    pub fn into_enum(self) -> Result<super::PokeEnum<'mem, 'facet>, ReflectError> {
138        match self.shape.ty {
139            Type::User(UserType::Enum(enum_type)) => Ok(super::PokeEnum {
140                value: self,
141                ty: enum_type,
142            }),
143            _ => Err(ReflectError::WasNotA {
144                expected: "enum",
145                actual: self.shape,
146            }),
147        }
148    }
149
150    /// Gets a reference to the underlying value.
151    ///
152    /// Returns an error if the shape doesn't match `T`.
153    pub fn get<T: Facet<'facet>>(&self) -> Result<&T, ReflectError> {
154        if self.shape != T::SHAPE {
155            return Err(ReflectError::WrongShape {
156                expected: self.shape,
157                actual: T::SHAPE,
158            });
159        }
160        Ok(unsafe { self.data.as_const().get::<T>() })
161    }
162
163    /// Gets a mutable reference to the underlying value.
164    ///
165    /// Returns an error if the shape doesn't match `T`.
166    pub fn get_mut<T: Facet<'facet>>(&mut self) -> Result<&mut T, ReflectError> {
167        if self.shape != T::SHAPE {
168            return Err(ReflectError::WrongShape {
169                expected: self.shape,
170                actual: T::SHAPE,
171            });
172        }
173        Ok(unsafe { self.data.as_mut::<T>() })
174    }
175
176    /// Sets the value to a new value.
177    ///
178    /// This replaces the entire value. The new value must have the same shape.
179    pub fn set<T: Facet<'facet>>(&mut self, value: T) -> Result<(), ReflectError> {
180        if self.shape != T::SHAPE {
181            return Err(ReflectError::WrongShape {
182                expected: self.shape,
183                actual: T::SHAPE,
184            });
185        }
186        unsafe {
187            // Drop the old value and write the new one
188            self.shape.call_drop_in_place(self.data);
189            core::ptr::write(self.data.as_mut_byte_ptr() as *mut T, value);
190        }
191        Ok(())
192    }
193
194    /// Converts this `Poke` into a read-only `Peek`.
195    #[inline]
196    pub fn as_peek(&self) -> crate::Peek<'_, 'facet> {
197        unsafe { crate::Peek::unchecked_new(self.data.as_const(), self.shape) }
198    }
199}
200
201impl core::fmt::Debug for Poke<'_, '_> {
202    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
203        write!(f, "Poke<{}>", self.shape)
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn poke_primitive_get_set() {
213        let mut x: i32 = 42;
214        let mut poke = Poke::new(&mut x);
215
216        assert_eq!(*poke.get::<i32>().unwrap(), 42);
217
218        poke.set(100i32).unwrap();
219        assert_eq!(x, 100);
220    }
221
222    #[test]
223    fn poke_primitive_get_mut() {
224        let mut x: i32 = 42;
225        let mut poke = Poke::new(&mut x);
226
227        *poke.get_mut::<i32>().unwrap() = 99;
228        assert_eq!(x, 99);
229    }
230
231    #[test]
232    fn poke_wrong_type_fails() {
233        let mut x: i32 = 42;
234        let poke = Poke::new(&mut x);
235
236        let result = poke.get::<u32>();
237        assert!(matches!(result, Err(ReflectError::WrongShape { .. })));
238    }
239
240    #[test]
241    fn poke_set_wrong_type_fails() {
242        let mut x: i32 = 42;
243        let mut poke = Poke::new(&mut x);
244
245        let result = poke.set(42u32);
246        assert!(matches!(result, Err(ReflectError::WrongShape { .. })));
247    }
248
249    #[test]
250    fn poke_string_drop_and_replace() {
251        // Wholesale replacement works on any type, including String
252        let mut s = String::from("hello");
253        let mut poke = Poke::new(&mut s);
254
255        poke.set(String::from("world")).unwrap();
256        assert_eq!(s, "world");
257    }
258}