facet_reflect/poke/
value.rs

1use crate::peek::Peek;
2use facet_core::{Facet, Opaque, OpaqueConst, OpaqueUninit, Shape, TryFromError, ValueVTable};
3
4/// A strongly-typed value writer that ensures type safety at compile-time
5pub struct TypedPokeValueUninit<'mem, T: Facet> {
6    poke_value: PokeValueUninit<'mem>,
7    _phantom: core::marker::PhantomData<T>,
8}
9
10impl<'mem, T: Facet> TypedPokeValueUninit<'mem, T> {
11    /// Create a new TypedPokeValue from a PokeValue
12    fn new(poke_value: PokeValueUninit<'mem>) -> Self {
13        Self {
14            poke_value,
15            _phantom: core::marker::PhantomData,
16        }
17    }
18
19    /// Place a value of type T in the space provided
20    pub fn put(self, value: T) -> PokeValue<'mem> {
21        // We already verified the shape matches T when we created this TypedPokeValue
22        let data = unsafe { self.poke_value.data.put(value) };
23        unsafe { PokeValue::new(data, self.poke_value.shape) }
24    }
25}
26
27/// Lets you write to a value (implements write-only [`ValueVTable`] proxies)
28pub struct PokeValueUninit<'mem> {
29    data: OpaqueUninit<'mem>,
30    shape: &'static Shape,
31}
32
33impl core::fmt::Debug for PokeValueUninit<'_> {
34    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        f.debug_struct("PokeValue")
36            .field("shape", &self.shape)
37            .finish_non_exhaustive()
38    }
39}
40
41impl<'mem> PokeValueUninit<'mem> {
42    #[inline(always)]
43    /// Coerce back into a `PokeValue`
44    pub fn into_value(self) -> Self {
45        self
46    }
47
48    /// Converts to a type-checked [`TypedPokeValue<T>`] if the shape matches type `T`
49    ///
50    /// Returns `None` if the shape doesn't match the type `T`.
51    pub fn typed<T: Facet>(self) -> Result<TypedPokeValueUninit<'mem, T>, Self> {
52        if self.shape.is_type::<T>() {
53            Ok(TypedPokeValueUninit::new(self))
54        } else {
55            Err(self)
56        }
57    }
58
59    /// Shape getter
60    #[inline(always)]
61    pub fn shape(&self) -> &'static Shape {
62        self.shape
63    }
64    /// Creates a value write-proxy from its essential components
65    ///
66    /// # Safety
67    ///
68    /// The data buffer must match the size and alignment of the shape.
69    pub(crate) unsafe fn new(data: OpaqueUninit<'mem>, shape: &'static Shape) -> Self {
70        Self { data, shape }
71    }
72
73    /// Gets the vtable for the value
74    #[inline(always)]
75    fn vtable(&self) -> &'static ValueVTable {
76        self.shape.vtable
77    }
78
79    /// Exposes the internal data buffer as a mutable reference
80    ///
81    /// # Safety
82    ///
83    /// The caller must ensure that they don't violate any invariants of the underlying type.
84    pub unsafe fn data(&mut self) -> OpaqueUninit<'mem> {
85        self.data
86    }
87
88    /// Attempts to convert a value from another type into this one
89    ///
90    /// Returns `Ok(Opaque)` if the conversion was successful, `Err((Self, TryFromError))` otherwise.
91    pub fn try_from<'src>(
92        self,
93        source: OpaqueConst<'src>,
94    ) -> Result<Opaque<'mem>, (Self, TryFromError)> {
95        if let Some(try_from_fn) = self.vtable().try_from {
96            match unsafe { try_from_fn(source, self.data) } {
97                Ok(built_val) => Ok(built_val),
98                Err(err) => Err((self, err)),
99            }
100        } else {
101            let shape = self.shape;
102            Err((self, TryFromError::Unimplemented(shape)))
103        }
104    }
105
106    /// Attempts to parse a string into this value
107    ///
108    /// Returns `Ok(Opaque)` if parsing was successful, `Err(Self)` otherwise.
109    pub fn parse(self, s: &str) -> Result<Opaque<'mem>, Self> {
110        if let Some(parse_fn) = self.vtable().parse {
111            match unsafe { parse_fn(s, self.data) } {
112                Ok(parsed_val) => Ok(parsed_val),
113                Err(_) => Err(self),
114            }
115        } else {
116            Err(self)
117        }
118    }
119
120    /// Place a value in the space provided. See also [`Self::typed`], which
121    /// is panic-free.
122    ///
123    /// This function places a value of type T into the destination space,
124    /// checking that T exactly matches the expected shape.
125    pub fn put<'src, T>(self, value: T) -> Opaque<'mem>
126    where
127        T: Facet + 'src,
128    {
129        self.shape.assert_type::<T>();
130        unsafe { self.data.put(value) }
131    }
132
133    /// Attempts to set the value to its default
134    ///
135    /// Returns `Ok(Opaque)` if setting to default was successful, `Err(Self)` otherwise.
136    pub fn default_in_place(self) -> Result<Opaque<'mem>, Self> {
137        if let Some(default_in_place_fn) = self.vtable().default_in_place {
138            let default_val = unsafe { default_in_place_fn(self.data) };
139            Ok(default_val)
140        } else {
141            Err(self)
142        }
143    }
144
145    /// Attempts to clone `source` into this value
146    ///
147    /// Returns `Ok(Peek)` if cloning was successful, `Err(Self)` otherwise.
148    pub fn clone_from<'src>(self, source: Peek<'src>) -> Result<Peek<'mem>, Self> {
149        if let Some(clone_fn) = self.vtable().clone_into {
150            let cloned_val = unsafe { clone_fn(source.data(), self.data) };
151            // Safe because the function will initialize our data if it returns Some
152            Ok(unsafe { Peek::unchecked_new(cloned_val.as_const(), self.shape) })
153        } else {
154            Err(self)
155        }
156    }
157}
158
159/// A strongly-typed value writer for initialized values that ensures type safety at compile-time
160pub struct TypedPokeValue<'mem, T: Facet> {
161    poke_value: PokeValue<'mem>,
162    _phantom: core::marker::PhantomData<T>,
163}
164
165impl<'mem, T: Facet> TypedPokeValue<'mem, T> {
166    /// Create a new TypedPokeValue from a PokeValue
167    fn new(poke_value: PokeValue<'mem>) -> Self {
168        Self {
169            poke_value,
170            _phantom: core::marker::PhantomData,
171        }
172    }
173
174    /// Replace the existing value with a new one of type T
175    pub fn replace(self, value: T) -> Opaque<'mem> {
176        // We already verified the shape matches T when we created this TypedPokeValue
177        unsafe { self.poke_value.data.replace(value) }
178    }
179}
180
181/// Lets you modify an initialized value (implements read-write [`ValueVTable`] proxies)
182pub struct PokeValue<'mem> {
183    data: Opaque<'mem>,
184    shape: &'static Shape,
185}
186
187impl core::fmt::Debug for PokeValue<'_> {
188    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
189        f.debug_struct("PokeValue")
190            .field("shape", &self.shape)
191            .field("data", &self.as_peek())
192            .finish()
193    }
194}
195
196impl<'mem> PokeValue<'mem> {
197    #[inline(always)]
198    /// Coerce back into a `PokeValue`
199    pub fn into_value(self) -> Self {
200        self
201    }
202
203    /// Converts to a type-checked [`TypedPokeValue<T>`] if the shape matches type `T`
204    ///
205    /// Returns `None` if the shape doesn't match the type `T`.
206    pub fn typed<T: Facet>(self) -> Result<TypedPokeValue<'mem, T>, Self> {
207        if self.shape.is_type::<T>() {
208            Ok(TypedPokeValue::new(self))
209        } else {
210            Err(self)
211        }
212    }
213
214    /// Shape getter
215    #[inline(always)]
216    pub fn shape(&self) -> &'static Shape {
217        self.shape
218    }
219
220    /// Creates a value write-proxy from its essential components
221    ///
222    /// # Safety
223    ///
224    /// The data must be a valid, initialized instance of the type described by shape.
225    pub(crate) unsafe fn new(data: Opaque<'mem>, shape: &'static Shape) -> Self {
226        Self { data, shape }
227    }
228
229    /// Gets the vtable for the value
230    #[inline(always)]
231    fn vtable(&self) -> &'static ValueVTable {
232        self.shape.vtable
233    }
234
235    /// Gets a read-only view of the value
236    pub fn as_peek(&self) -> Peek<'_> {
237        unsafe { Peek::unchecked_new(self.data.as_const(), self.shape) }
238    }
239
240    /// Exposes the internal data buffer as a mutable reference
241    ///
242    /// # Safety
243    ///
244    /// The caller must ensure that they don't violate any invariants of the underlying type.
245    pub unsafe fn data(&mut self) -> Opaque<'mem> {
246        self.data
247    }
248
249    /// Replace the current value with a new one of the same type
250    ///
251    /// This function replaces the existing value with a new one of type T,
252    /// checking that T exactly matches the expected shape.
253    pub fn replace<'src, T>(self, value: T) -> Opaque<'mem>
254    where
255        T: Facet + 'src,
256    {
257        self.shape.assert_type::<T>();
258        unsafe { self.data.replace(value) }
259    }
260
261    /// Format the value using its Debug implementation
262    pub fn debug_fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
263        if let Some(debug_fn) = self.vtable().debug {
264            unsafe { debug_fn(self.data.as_const(), f) }
265        } else {
266            f.write_str("<no debug impl>")
267        }
268    }
269
270    /// Format the value using its Display implementation
271    pub fn display_fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
272        if let Some(display_fn) = self.vtable().display {
273            unsafe { display_fn(self.data.as_const(), f) }
274        } else {
275            f.write_str("<no display impl>")
276        }
277    }
278}