facet_reflect/peek/
value.rs

1use core::{cmp::Ordering, marker::PhantomData};
2use facet_core::{
3    Def, Facet, PointerType, PtrConst, PtrMut, Shape, StructKind, Type, TypeNameOpts, UserType,
4    ValueVTable,
5};
6
7use crate::{ReflectError, ScalarType};
8
9use super::{
10    ListLikeDef, PeekEnum, PeekList, PeekListLike, PeekMap, PeekSmartPointer, PeekStruct,
11    PeekTuple, tuple::TupleType,
12};
13
14/// A unique identifier for a peek value
15#[derive(Clone, Copy, PartialEq, Eq, Hash)]
16pub struct ValueId<'shape> {
17    pub(crate) shape: &'shape Shape<'shape>,
18    pub(crate) ptr: *const u8,
19}
20
21impl<'shape> ValueId<'shape> {
22    pub(crate) fn new(shape: &'shape Shape<'shape>, ptr: *const u8) -> Self {
23        Self { shape, ptr }
24    }
25}
26
27impl core::fmt::Display for ValueId<'_> {
28    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
29        write!(f, "{}@{:p}", self.shape, self.ptr)
30    }
31}
32
33impl core::fmt::Debug for ValueId<'_> {
34    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        core::fmt::Display::fmt(self, f)
36    }
37}
38
39/// Lets you read from a value (implements read-only [`ValueVTable`] proxies)
40#[derive(Clone, Copy)]
41pub struct Peek<'mem, 'facet, 'shape> {
42    /// Underlying data
43    pub(crate) data: PtrConst<'mem>,
44
45    /// Shape of the value
46    pub(crate) shape: &'shape Shape<'shape>,
47
48    invariant: PhantomData<fn(&'facet ()) -> &'facet ()>,
49}
50
51impl<'mem, 'facet, 'shape> Peek<'mem, 'facet, 'shape> {
52    /// Creates a new `PeekValue` instance for a value of type `T`.
53    pub fn new<T: Facet<'facet>>(t: &'mem T) -> Self {
54        Self {
55            data: PtrConst::new(t as *const T),
56            shape: T::SHAPE,
57            invariant: PhantomData,
58        }
59    }
60
61    /// Creates a new `PeekValue` instance without checking the type.
62    ///
63    /// # Safety
64    ///
65    /// This function is unsafe because it doesn't check if the provided data
66    /// and shape are compatible. The caller must ensure that the data is valid
67    /// for the given shape.
68    pub unsafe fn unchecked_new(data: PtrConst<'mem>, shape: &'shape Shape<'shape>) -> Self {
69        Self {
70            data,
71            shape,
72            invariant: PhantomData,
73        }
74    }
75
76    /// Returns the vtable
77    #[inline(always)]
78    pub fn vtable(&self) -> &'shape ValueVTable {
79        self.shape.vtable
80    }
81
82    /// Returns a unique identifier for this value, usable for cycle detection
83    pub fn id(&self) -> ValueId<'shape> {
84        ValueId::new(self.shape, self.data.as_byte_ptr())
85    }
86
87    /// Returns true if the two values are pointer-equal
88    #[inline]
89    pub fn ptr_eq(&self, other: &Peek<'_, '_, '_>) -> bool {
90        self.data.as_byte_ptr() == other.data.as_byte_ptr()
91    }
92
93    /// Returns true if this scalar is equal to the other scalar
94    ///
95    /// # Returns
96    ///
97    /// `false` if equality comparison is not supported for this scalar type
98    #[inline]
99    pub fn partial_eq(&self, other: &Peek<'_, '_, '_>) -> Option<bool> {
100        unsafe { (self.shape.vtable.partial_eq)().map(|eq_fn| eq_fn(self.data, other.data)) }
101    }
102
103    /// Compares this scalar with another and returns their ordering
104    ///
105    /// # Returns
106    ///
107    /// `None` if comparison is not supported for this scalar type
108    #[inline]
109    pub fn partial_cmp(&self, other: &Peek<'_, '_, '_>) -> Option<Ordering> {
110        unsafe {
111            (self.shape.vtable.partial_ord)()
112                .and_then(|partial_ord_fn| partial_ord_fn(self.data, other.data))
113        }
114    }
115
116    /// Hashes this scalar
117    ///
118    /// # Returns
119    ///
120    /// `false` if hashing is not supported for this scalar type, `true` otherwise
121    #[inline(always)]
122    pub fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) -> bool {
123        unsafe {
124            if let Some(hash_fn) = (self.shape.vtable.hash)() {
125                let hasher_opaque = PtrMut::new(hasher);
126                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
127                    opaque.as_mut::<H>().write(bytes)
128                });
129                true
130            } else {
131                false
132            }
133        }
134    }
135
136    /// Returns the type name of this scalar
137    ///
138    /// # Arguments
139    ///
140    /// * `f` - A mutable reference to a `core::fmt::Formatter`
141    /// * `opts` - The `TypeNameOpts` to use for formatting
142    ///
143    /// # Returns
144    ///
145    /// The result of the type name formatting
146    #[inline(always)]
147    pub fn type_name(
148        &self,
149        f: &mut core::fmt::Formatter<'_>,
150        opts: TypeNameOpts,
151    ) -> core::fmt::Result {
152        (self.shape.vtable.type_name)(f, opts)
153    }
154
155    /// Returns the shape
156    #[inline(always)]
157    pub const fn shape(&self) -> &'shape Shape<'shape> {
158        self.shape
159    }
160
161    /// Returns the data
162    #[inline(always)]
163    pub const fn data(&self) -> PtrConst<'mem> {
164        self.data
165    }
166
167    /// Get the scalar type if set.
168    pub fn scalar_type(&self) -> Option<ScalarType> {
169        ScalarType::try_from_shape(self.shape)
170    }
171
172    /// Read the value from memory into a Rust value.
173    ///
174    /// # Panics
175    ///
176    /// Panics if the shape doesn't match the type `T`.
177    pub fn get<T: Facet<'facet>>(&self) -> Result<&T, ReflectError<'shape>> {
178        if self.shape != T::SHAPE {
179            Err(ReflectError::WrongShape {
180                expected: self.shape,
181                actual: T::SHAPE,
182            })
183        } else {
184            Ok(unsafe { self.data.get::<T>() })
185        }
186    }
187
188    /// Try to get the value as a string if it's a string type
189    /// Returns None if the value is not a string or couldn't be extracted
190    pub fn as_str(&self) -> Option<&'mem str> {
191        let peek = self.innermost_peek();
192        if let Some(ScalarType::Str) = peek.scalar_type() {
193            unsafe { Some(peek.data.get::<&str>()) }
194        } else if let Some(ScalarType::String) = peek.scalar_type() {
195            unsafe { Some(peek.data.get::<alloc::string::String>().as_str()) }
196        } else if let Type::Pointer(PointerType::Reference(vpt)) = peek.shape.ty {
197            let target_shape = (vpt.target)();
198            if let Some(ScalarType::Str) = ScalarType::try_from_shape(target_shape) {
199                unsafe { Some(peek.data.get::<&str>()) }
200            } else {
201                None
202            }
203        } else {
204            None
205        }
206    }
207
208    /// Tries to identify this value as a struct
209    pub fn into_struct(self) -> Result<PeekStruct<'mem, 'facet, 'shape>, ReflectError<'shape>> {
210        if let Type::User(UserType::Struct(ty)) = self.shape.ty {
211            Ok(PeekStruct { value: self, ty })
212        } else {
213            Err(ReflectError::WasNotA {
214                expected: "struct",
215                actual: self.shape,
216            })
217        }
218    }
219
220    /// Tries to identify this value as an enum
221    pub fn into_enum(self) -> Result<PeekEnum<'mem, 'facet, 'shape>, ReflectError<'shape>> {
222        if let Type::User(UserType::Enum(ty)) = self.shape.ty {
223            Ok(PeekEnum { value: self, ty })
224        } else {
225            Err(ReflectError::WasNotA {
226                expected: "enum",
227                actual: self.shape,
228            })
229        }
230    }
231
232    /// Tries to identify this value as a map
233    pub fn into_map(self) -> Result<PeekMap<'mem, 'facet, 'shape>, ReflectError<'shape>> {
234        if let Def::Map(def) = self.shape.def {
235            Ok(PeekMap { value: self, def })
236        } else {
237            Err(ReflectError::WasNotA {
238                expected: "map",
239                actual: self.shape,
240            })
241        }
242    }
243
244    /// Tries to identify this value as a list
245    pub fn into_list(self) -> Result<PeekList<'mem, 'facet, 'shape>, ReflectError<'shape>> {
246        if let Def::List(def) = self.shape.def {
247            return Ok(PeekList { value: self, def });
248        }
249
250        Err(ReflectError::WasNotA {
251            expected: "list",
252            actual: self.shape,
253        })
254    }
255
256    /// Tries to identify this value as a list, array or slice
257    pub fn into_list_like(
258        self,
259    ) -> Result<PeekListLike<'mem, 'facet, 'shape>, ReflectError<'shape>> {
260        match self.shape.def {
261            Def::List(def) => Ok(PeekListLike::new(self, ListLikeDef::List(def))),
262            Def::Array(def) => Ok(PeekListLike::new(self, ListLikeDef::Array(def))),
263            _ => {
264                // &[i32] is actually a _pointer_ to a slice.
265                match self.shape.ty {
266                    Type::Pointer(ptr) => match ptr {
267                        PointerType::Reference(vpt) | PointerType::Raw(vpt) => {
268                            let target = (vpt.target)();
269                            match target.def {
270                                Def::Slice(def) => {
271                                    return Ok(PeekListLike::new(self, ListLikeDef::Slice(def)));
272                                }
273                                _ => {
274                                    // well it's not list-like then
275                                }
276                            }
277                        }
278                        PointerType::Function(_) => {
279                            // well that's not a list-like
280                        }
281                    },
282                    _ => {
283                        // well that's not a list-like either
284                    }
285                }
286
287                Err(ReflectError::WasNotA {
288                    expected: "list, array or slice",
289                    actual: self.shape,
290                })
291            }
292        }
293    }
294
295    /// Tries to identify this value as a smart pointer
296    pub fn into_smart_pointer(
297        self,
298    ) -> Result<PeekSmartPointer<'mem, 'facet, 'shape>, ReflectError<'shape>> {
299        if let Def::SmartPointer(def) = self.shape.def {
300            Ok(PeekSmartPointer { value: self, def })
301        } else {
302            Err(ReflectError::WasNotA {
303                expected: "smart pointer",
304                actual: self.shape,
305            })
306        }
307    }
308
309    /// Tries to identify this value as an option
310    pub fn into_option(
311        self,
312    ) -> Result<super::PeekOption<'mem, 'facet, 'shape>, ReflectError<'shape>> {
313        if let Def::Option(def) = self.shape.def {
314            Ok(super::PeekOption { value: self, def })
315        } else {
316            Err(ReflectError::WasNotA {
317                expected: "option",
318                actual: self.shape,
319            })
320        }
321    }
322
323    /// Tries to identify this value as a tuple
324    pub fn into_tuple(self) -> Result<PeekTuple<'mem, 'facet, 'shape>, ReflectError<'shape>> {
325        if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
326            if struct_type.kind == StructKind::Tuple {
327                Ok(PeekTuple {
328                    value: self,
329                    ty: TupleType {
330                        fields: struct_type.fields,
331                    },
332                })
333            } else {
334                Err(ReflectError::WasNotA {
335                    expected: "tuple",
336                    actual: self.shape,
337                })
338            }
339        } else {
340            Err(ReflectError::WasNotA {
341                expected: "tuple",
342                actual: self.shape,
343            })
344        }
345    }
346
347    /// Tries to return the innermost value — useful for serialization. For example, we serialize a `NonZero<u8>` the same
348    /// as a `u8`. Similarly, we serialize a `Utf8PathBuf` the same as a `String.
349    ///
350    /// Returns a `Peek` to the innermost value, unwrapping transparent wrappers recursively.
351    /// For example, this will peel through newtype wrappers or smart pointers that have an `inner`.
352    pub fn innermost_peek(self) -> Self {
353        let mut current_peek = self;
354        while let (Some(try_borrow_inner_fn), Some(inner_shape)) = (
355            (current_peek.shape.vtable.try_borrow_inner)(),
356            current_peek.shape.inner,
357        ) {
358            unsafe {
359                let inner_data = try_borrow_inner_fn(current_peek.data).unwrap_or_else(|e| {
360                    panic!("innermost_peek: try_borrow_inner returned an error! was trying to go from {} to {}. error: {e}", current_peek.shape,
361                        inner_shape())
362                });
363
364                current_peek = Peek {
365                    data: inner_data,
366                    shape: inner_shape(),
367                    invariant: PhantomData,
368                };
369            }
370        }
371        current_peek
372    }
373}
374
375impl<'mem, 'facet, 'shape> core::fmt::Display for Peek<'mem, 'facet, 'shape> {
376    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
377        if let Some(display_fn) = (self.vtable().display)() {
378            unsafe { display_fn(self.data, f) }
379        } else {
380            write!(f, "⟨{}⟩", self.shape)
381        }
382    }
383}
384
385impl<'mem, 'facet, 'shape> core::fmt::Debug for Peek<'mem, 'facet, 'shape> {
386    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
387        if let Some(debug_fn) = (self.vtable().debug)() {
388            unsafe { debug_fn(self.data, f) }
389        } else {
390            write!(f, "⟨{}⟩", self.shape)
391        }
392    }
393}
394
395impl<'mem, 'facet, 'shape> core::cmp::PartialEq for Peek<'mem, 'facet, 'shape> {
396    fn eq(&self, other: &Self) -> bool {
397        if self.shape != other.shape {
398            return false;
399        }
400        let eq_fn = match (self.shape.vtable.partial_eq)() {
401            Some(eq_fn) => eq_fn,
402            None => return false,
403        };
404        unsafe { eq_fn(self.data, other.data) }
405    }
406}
407
408impl<'mem, 'facet, 'shape> core::cmp::PartialOrd for Peek<'mem, 'facet, 'shape> {
409    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
410        if self.shape != other.shape {
411            return None;
412        }
413        let partial_ord_fn = (self.shape.vtable.partial_ord)()?;
414        unsafe { partial_ord_fn(self.data, other.data) }
415    }
416}
417
418impl<'mem, 'facet, 'shape> core::hash::Hash for Peek<'mem, 'facet, 'shape> {
419    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
420        if let Some(hash_fn) = (self.shape.vtable.hash)() {
421            let hasher_opaque = PtrMut::new(hasher);
422            unsafe {
423                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
424                    opaque.as_mut::<H>().write(bytes)
425                })
426            };
427        } else {
428            panic!("Hashing is not supported for this shape");
429        }
430    }
431}