Skip to main content

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::{PokeList, 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 with respect to 'facet (same reasoning as Peek)
54    /// Covariant with respect to '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 const fn shape(&self) -> &'static Shape {
89        self.shape
90    }
91
92    /// Returns a const pointer to the underlying data.
93    #[inline(always)]
94    pub const 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 const fn data_mut(&mut self) -> PtrMut {
101        self.data
102    }
103
104    /// Returns true if this value is a struct.
105    #[inline]
106    pub const 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 const 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 const 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 const 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 const 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    /// Converts this into a `PokeList` if the value is a list.
151    #[inline]
152    pub const fn into_list(self) -> Result<PokeList<'mem, 'facet>, ReflectError> {
153        if let Def::List(def) = self.shape.def {
154            // SAFETY: The ListDef comes from self.shape.def, where self.shape is obtained
155            // from a trusted source (either T::SHAPE from the Facet trait, or validated
156            // through other safe constructors). The vtable is therefore trusted.
157            return Ok(unsafe { PokeList::new(self, def) });
158        }
159
160        Err(ReflectError::WasNotA {
161            expected: "list",
162            actual: self.shape,
163        })
164    }
165
166    /// Gets a reference to the underlying value.
167    ///
168    /// Returns an error if the shape doesn't match `T`.
169    pub fn get<T: Facet<'facet>>(&self) -> Result<&T, ReflectError> {
170        if self.shape != T::SHAPE {
171            return Err(ReflectError::WrongShape {
172                expected: self.shape,
173                actual: T::SHAPE,
174            });
175        }
176        Ok(unsafe { self.data.as_const().get::<T>() })
177    }
178
179    /// Gets a mutable reference to the underlying value.
180    ///
181    /// Returns an error if the shape doesn't match `T`.
182    pub fn get_mut<T: Facet<'facet>>(&mut self) -> Result<&mut T, ReflectError> {
183        if self.shape != T::SHAPE {
184            return Err(ReflectError::WrongShape {
185                expected: self.shape,
186                actual: T::SHAPE,
187            });
188        }
189        Ok(unsafe { self.data.as_mut::<T>() })
190    }
191
192    /// Sets the value to a new value.
193    ///
194    /// This replaces the entire value. The new value must have the same shape.
195    pub fn set<T: Facet<'facet>>(&mut self, value: T) -> Result<(), ReflectError> {
196        if self.shape != T::SHAPE {
197            return Err(ReflectError::WrongShape {
198                expected: self.shape,
199                actual: T::SHAPE,
200            });
201        }
202        unsafe {
203            // Drop the old value and write the new one
204            self.shape.call_drop_in_place(self.data);
205            core::ptr::write(self.data.as_mut_byte_ptr() as *mut T, value);
206        }
207        Ok(())
208    }
209
210    /// Converts this `Poke` into a read-only `Peek`.
211    #[inline]
212    pub fn as_peek(&self) -> crate::Peek<'_, 'facet> {
213        unsafe { crate::Peek::unchecked_new(self.data.as_const(), self.shape) }
214    }
215}
216
217impl core::fmt::Debug for Poke<'_, '_> {
218    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
219        write!(f, "Poke<{}>", self.shape)
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    use super::*;
226
227    #[test]
228    fn poke_primitive_get_set() {
229        let mut x: i32 = 42;
230        let mut poke = Poke::new(&mut x);
231
232        assert_eq!(*poke.get::<i32>().unwrap(), 42);
233
234        poke.set(100i32).unwrap();
235        assert_eq!(x, 100);
236    }
237
238    #[test]
239    fn poke_primitive_get_mut() {
240        let mut x: i32 = 42;
241        let mut poke = Poke::new(&mut x);
242
243        *poke.get_mut::<i32>().unwrap() = 99;
244        assert_eq!(x, 99);
245    }
246
247    #[test]
248    fn poke_wrong_type_fails() {
249        let mut x: i32 = 42;
250        let poke = Poke::new(&mut x);
251
252        let result = poke.get::<u32>();
253        assert!(matches!(result, Err(ReflectError::WrongShape { .. })));
254    }
255
256    #[test]
257    fn poke_set_wrong_type_fails() {
258        let mut x: i32 = 42;
259        let mut poke = Poke::new(&mut x);
260
261        let result = poke.set(42u32);
262        assert!(matches!(result, Err(ReflectError::WrongShape { .. })));
263    }
264
265    #[test]
266    fn poke_string_drop_and_replace() {
267        // Wholesale replacement works on any type, including String
268        let mut s = String::from("hello");
269        let mut poke = Poke::new(&mut s);
270
271        poke.set(String::from("world")).unwrap();
272        assert_eq!(s, "world");
273    }
274}