facet_reflect/peek/
value.rs

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