facet_reflect/poke/
value.rs

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