facet_reflect/peek/
value.rs

1use core::cmp::Ordering;
2use facet_core::{Def, Facet, PtrConst, PtrMut, Shape, TypeNameOpts, ValueVTable};
3
4use crate::{ReflectError, ScalarType};
5
6use super::{PeekEnum, PeekList, PeekMap, PeekSmartPointer, PeekStruct};
7
8/// A unique identifier for a peek value
9#[derive(Clone, Copy, PartialEq, Eq, Hash)]
10pub struct ValueId {
11    pub(crate) shape: &'static Shape,
12    pub(crate) ptr: *const u8,
13}
14
15impl ValueId {
16    pub(crate) fn new(shape: &'static Shape, ptr: *const u8) -> Self {
17        Self { shape, ptr }
18    }
19}
20
21impl core::fmt::Display for ValueId {
22    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
23        write!(f, "{}@{:p}", self.shape, self.ptr)
24    }
25}
26
27impl core::fmt::Debug for ValueId {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        core::fmt::Display::fmt(self, f)
30    }
31}
32
33/// Lets you read from a value (implements read-only [`ValueVTable`] proxies)
34#[derive(Clone, Copy)]
35pub struct Peek<'mem> {
36    /// Underlying data
37    pub(crate) data: PtrConst<'mem>,
38
39    /// Shape of the value
40    pub(crate) shape: &'static Shape,
41}
42
43impl<'mem> Peek<'mem> {
44    /// Creates a new `PeekValue` instance for a value of type `T`.
45    pub fn new<T: Facet + 'mem>(t: &'mem T) -> Self {
46        Self {
47            data: PtrConst::new(t as *const T),
48            shape: T::SHAPE,
49        }
50    }
51
52    /// Creates a new `PeekValue` instance without checking the type.
53    ///
54    /// # Safety
55    ///
56    /// This function is unsafe because it doesn't check if the provided data
57    /// and shape are compatible. The caller must ensure that the data is valid
58    /// for the given shape.
59    pub unsafe fn unchecked_new(data: PtrConst<'mem>, shape: &'static Shape) -> Self {
60        Self { data, shape }
61    }
62
63    /// Returns the vtable
64    #[inline(always)]
65    fn vtable(&self) -> &'static ValueVTable {
66        self.shape.vtable
67    }
68
69    /// Returns a unique identifier for this value, usable for cycle detection
70    pub fn id(&self) -> ValueId {
71        ValueId::new(self.shape, self.data.as_byte_ptr())
72    }
73
74    /// Returns true if the two values are pointer-equal
75    #[inline]
76    pub fn ptr_eq(&self, other: &Peek<'_>) -> bool {
77        self.data.as_byte_ptr() == other.data.as_byte_ptr()
78    }
79
80    /// Returns true if this scalar is equal to the other scalar
81    ///
82    /// # Returns
83    ///
84    /// `false` if equality comparison is not supported for this scalar type
85    #[inline]
86    pub fn eq(&self, other: &Peek<'_>) -> Option<bool> {
87        unsafe {
88            self.shape
89                .vtable
90                .eq
91                .map(|eq_fn| eq_fn(self.data, other.data))
92        }
93    }
94
95    /// Compares this scalar with another and returns their ordering
96    ///
97    /// # Returns
98    ///
99    /// `None` if comparison is not supported for this scalar type
100    #[inline]
101    pub fn partial_cmp(&self, other: &Peek<'_>) -> Option<Ordering> {
102        unsafe {
103            self.shape
104                .vtable
105                .partial_ord
106                .and_then(|partial_ord_fn| partial_ord_fn(self.data, other.data))
107        }
108    }
109
110    /// Hashes this scalar
111    ///
112    /// # Returns
113    ///
114    /// `false` if hashing is not supported for this scalar type, `true` otherwise
115    #[inline(always)]
116    pub fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) -> bool {
117        unsafe {
118            if let Some(hash_fn) = self.shape.vtable.hash {
119                let hasher_opaque = PtrMut::new(hasher);
120                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
121                    opaque.as_mut::<H>().write(bytes)
122                });
123                true
124            } else {
125                false
126            }
127        }
128    }
129
130    /// Returns the type name of this scalar
131    ///
132    /// # Arguments
133    ///
134    /// * `f` - A mutable reference to a `core::fmt::Formatter`
135    /// * `opts` - The `TypeNameOpts` to use for formatting
136    ///
137    /// # Returns
138    ///
139    /// The result of the type name formatting
140    #[inline(always)]
141    pub fn type_name(
142        &self,
143        f: &mut core::fmt::Formatter<'_>,
144        opts: TypeNameOpts,
145    ) -> core::fmt::Result {
146        (self.shape.vtable.type_name)(f, opts)
147    }
148
149    /// Returns the shape
150    #[inline(always)]
151    pub const fn shape(&self) -> &'static Shape {
152        self.shape
153    }
154
155    /// Returns the data
156    #[inline(always)]
157    pub const fn data(&self) -> PtrConst<'mem> {
158        self.data
159    }
160
161    /// Get the scalar type if set.
162    pub fn scalar_type(&self) -> Option<ScalarType> {
163        ScalarType::try_from_shape(self.shape)
164    }
165
166    /// Read the value from memory into a Rust value.
167    ///
168    /// # Panics
169    ///
170    /// Panics if the shape doesn't match the type `T`.
171    pub fn get<T: Facet>(&self) -> Result<&T, ReflectError> {
172        if self.shape != T::SHAPE {
173            Err(ReflectError::WrongShape {
174                expected: self.shape,
175                actual: T::SHAPE,
176            })
177        } else {
178            Ok(unsafe { self.data.get::<T>() })
179        }
180    }
181
182    /// Tries to identify this value as a struct
183    pub fn into_struct(self) -> Result<PeekStruct<'mem>, ReflectError> {
184        if let Def::Struct(def) = self.shape.def {
185            Ok(PeekStruct { value: self, def })
186        } else {
187            Err(ReflectError::WasNotA {
188                expected: "struct",
189                actual: self.shape,
190            })
191        }
192    }
193
194    /// Tries to identify this value as an enum
195    pub fn into_enum(self) -> Result<PeekEnum<'mem>, ReflectError> {
196        if let Def::Enum(def) = self.shape.def {
197            Ok(PeekEnum { value: self, def })
198        } else {
199            Err(ReflectError::WasNotA {
200                expected: "enum",
201                actual: self.shape,
202            })
203        }
204    }
205
206    /// Tries to identify this value as a map
207    pub fn into_map(self) -> Result<PeekMap<'mem>, ReflectError> {
208        if let Def::Map(def) = self.shape.def {
209            Ok(PeekMap { value: self, def })
210        } else {
211            Err(ReflectError::WasNotA {
212                expected: "map",
213                actual: self.shape,
214            })
215        }
216    }
217
218    /// Tries to identify this value as a list
219    pub fn into_list(self) -> Result<PeekList<'mem>, ReflectError> {
220        if let Def::List(def) = self.shape.def {
221            Ok(PeekList { value: self, def })
222        } else {
223            Err(ReflectError::WasNotA {
224                expected: "list",
225                actual: self.shape,
226            })
227        }
228    }
229
230    /// Tries to identify this value as a smart pointer
231    pub fn into_smart_pointer(self) -> Result<PeekSmartPointer<'mem>, ReflectError> {
232        if let Def::SmartPointer(def) = self.shape.def {
233            Ok(PeekSmartPointer { value: self, def })
234        } else {
235            Err(ReflectError::WasNotA {
236                expected: "smart pointer",
237                actual: self.shape,
238            })
239        }
240    }
241
242    /// Tries to identify this value as an option
243    pub fn into_option(self) -> Result<super::PeekOption<'mem>, ReflectError> {
244        if let Def::Option(def) = self.shape.def {
245            Ok(super::PeekOption { value: self, def })
246        } else {
247            Err(ReflectError::WasNotA {
248                expected: "option",
249                actual: self.shape,
250            })
251        }
252    }
253}
254
255impl core::fmt::Display for Peek<'_> {
256    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257        if let Some(display_fn) = self.vtable().display {
258            unsafe { display_fn(self.data, f) }
259        } else {
260            write!(f, "⟨{}⟩", self.shape)
261        }
262    }
263}
264
265impl core::fmt::Debug for Peek<'_> {
266    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
267        if let Some(debug_fn) = self.vtable().debug {
268            unsafe { debug_fn(self.data, f) }
269        } else {
270            write!(f, "⟨{}⟩", self.shape)
271        }
272    }
273}
274
275impl core::cmp::PartialEq for Peek<'_> {
276    fn eq(&self, other: &Self) -> bool {
277        if self.shape != other.shape {
278            return false;
279        }
280        let eq_fn = match self.shape.vtable.eq {
281            Some(eq_fn) => eq_fn,
282            None => return false,
283        };
284        unsafe { eq_fn(self.data, other.data) }
285    }
286}
287
288impl core::cmp::PartialOrd for Peek<'_> {
289    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
290        if self.shape != other.shape {
291            return None;
292        }
293        let partial_ord_fn = self.shape.vtable.partial_ord?;
294        unsafe { partial_ord_fn(self.data, other.data) }
295    }
296}
297
298impl core::hash::Hash for Peek<'_> {
299    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
300        if let Some(hash_fn) = self.shape.vtable.hash {
301            let hasher_opaque = PtrMut::new(hasher);
302            unsafe {
303                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
304                    opaque.as_mut::<H>().write(bytes)
305                })
306            };
307        } else {
308            panic!("Hashing is not supported for this shape");
309        }
310    }
311}