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    /// Try to get the value as a byte slice if it's a &[u8] type
209    /// Returns None if the value is not a byte slice or couldn't be extracted
210    pub fn as_bytes(&self) -> Option<&'mem [u8]> {
211        // Check if it's a direct &[u8]
212        if let Type::Pointer(PointerType::Reference(vpt)) = self.shape.ty {
213            let target_shape = (vpt.target)();
214            if let Def::Slice(sd) = target_shape.def {
215                if sd.t().is_type::<u8>() {
216                    unsafe { return Some(self.data.get::<&[u8]>()) }
217                }
218            }
219        }
220        None
221    }
222
223    /// Tries to identify this value as a struct
224    pub fn into_struct(self) -> Result<PeekStruct<'mem, 'facet, 'shape>, ReflectError<'shape>> {
225        if let Type::User(UserType::Struct(ty)) = self.shape.ty {
226            Ok(PeekStruct { value: self, ty })
227        } else {
228            Err(ReflectError::WasNotA {
229                expected: "struct",
230                actual: self.shape,
231            })
232        }
233    }
234
235    /// Tries to identify this value as an enum
236    pub fn into_enum(self) -> Result<PeekEnum<'mem, 'facet, 'shape>, ReflectError<'shape>> {
237        if let Type::User(UserType::Enum(ty)) = self.shape.ty {
238            Ok(PeekEnum { value: self, ty })
239        } else {
240            Err(ReflectError::WasNotA {
241                expected: "enum",
242                actual: self.shape,
243            })
244        }
245    }
246
247    /// Tries to identify this value as a map
248    pub fn into_map(self) -> Result<PeekMap<'mem, 'facet, 'shape>, ReflectError<'shape>> {
249        if let Def::Map(def) = self.shape.def {
250            Ok(PeekMap { value: self, def })
251        } else {
252            Err(ReflectError::WasNotA {
253                expected: "map",
254                actual: self.shape,
255            })
256        }
257    }
258
259    /// Tries to identify this value as a list
260    pub fn into_list(self) -> Result<PeekList<'mem, 'facet, 'shape>, ReflectError<'shape>> {
261        if let Def::List(def) = self.shape.def {
262            return Ok(PeekList { value: self, def });
263        }
264
265        Err(ReflectError::WasNotA {
266            expected: "list",
267            actual: self.shape,
268        })
269    }
270
271    /// Tries to identify this value as a list, array or slice
272    pub fn into_list_like(
273        self,
274    ) -> Result<PeekListLike<'mem, 'facet, 'shape>, ReflectError<'shape>> {
275        match self.shape.def {
276            Def::List(def) => Ok(PeekListLike::new(self, ListLikeDef::List(def))),
277            Def::Array(def) => Ok(PeekListLike::new(self, ListLikeDef::Array(def))),
278            _ => {
279                // &[i32] is actually a _pointer_ to a slice.
280                match self.shape.ty {
281                    Type::Pointer(ptr) => match ptr {
282                        PointerType::Reference(vpt) | PointerType::Raw(vpt) => {
283                            let target = (vpt.target)();
284                            match target.def {
285                                Def::Slice(def) => {
286                                    return Ok(PeekListLike::new(self, ListLikeDef::Slice(def)));
287                                }
288                                _ => {
289                                    // well it's not list-like then
290                                }
291                            }
292                        }
293                        PointerType::Function(_) => {
294                            // well that's not a list-like
295                        }
296                    },
297                    _ => {
298                        // well that's not a list-like either
299                    }
300                }
301
302                Err(ReflectError::WasNotA {
303                    expected: "list, array or slice",
304                    actual: self.shape,
305                })
306            }
307        }
308    }
309
310    /// Tries to identify this value as a smart pointer
311    pub fn into_smart_pointer(
312        self,
313    ) -> Result<PeekSmartPointer<'mem, 'facet, 'shape>, ReflectError<'shape>> {
314        if let Def::SmartPointer(def) = self.shape.def {
315            Ok(PeekSmartPointer { value: self, def })
316        } else {
317            Err(ReflectError::WasNotA {
318                expected: "smart pointer",
319                actual: self.shape,
320            })
321        }
322    }
323
324    /// Tries to identify this value as an option
325    pub fn into_option(
326        self,
327    ) -> Result<super::PeekOption<'mem, 'facet, 'shape>, ReflectError<'shape>> {
328        if let Def::Option(def) = self.shape.def {
329            Ok(super::PeekOption { value: self, def })
330        } else {
331            Err(ReflectError::WasNotA {
332                expected: "option",
333                actual: self.shape,
334            })
335        }
336    }
337
338    /// Tries to identify this value as a tuple
339    pub fn into_tuple(self) -> Result<PeekTuple<'mem, 'facet, 'shape>, ReflectError<'shape>> {
340        if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
341            if struct_type.kind == StructKind::Tuple {
342                Ok(PeekTuple {
343                    value: self,
344                    ty: TupleType {
345                        fields: struct_type.fields,
346                    },
347                })
348            } else {
349                Err(ReflectError::WasNotA {
350                    expected: "tuple",
351                    actual: self.shape,
352                })
353            }
354        } else {
355            Err(ReflectError::WasNotA {
356                expected: "tuple",
357                actual: self.shape,
358            })
359        }
360    }
361
362    /// Tries to return the innermost value — useful for serialization. For example, we serialize a `NonZero<u8>` the same
363    /// as a `u8`. Similarly, we serialize a `Utf8PathBuf` the same as a `String.
364    ///
365    /// Returns a `Peek` to the innermost value, unwrapping transparent wrappers recursively.
366    /// For example, this will peel through newtype wrappers or smart pointers that have an `inner`.
367    pub fn innermost_peek(self) -> Self {
368        let mut current_peek = self;
369        while let (Some(try_borrow_inner_fn), Some(inner_shape)) = (
370            (current_peek.shape.vtable.try_borrow_inner)(),
371            current_peek.shape.inner,
372        ) {
373            unsafe {
374                let inner_data = try_borrow_inner_fn(current_peek.data).unwrap_or_else(|e| {
375                    panic!("innermost_peek: try_borrow_inner returned an error! was trying to go from {} to {}. error: {e}", current_peek.shape,
376                        inner_shape())
377                });
378
379                current_peek = Peek {
380                    data: inner_data,
381                    shape: inner_shape(),
382                    invariant: PhantomData,
383                };
384            }
385        }
386        current_peek
387    }
388}
389
390impl<'mem, 'facet, 'shape> core::fmt::Display for Peek<'mem, 'facet, 'shape> {
391    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
392        if let Some(display_fn) = (self.vtable().display)() {
393            unsafe { display_fn(self.data, f) }
394        } else {
395            write!(f, "⟨{}⟩", self.shape)
396        }
397    }
398}
399
400impl<'mem, 'facet, 'shape> core::fmt::Debug for Peek<'mem, 'facet, 'shape> {
401    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
402        if let Some(debug_fn) = (self.vtable().debug)() {
403            unsafe { debug_fn(self.data, f) }
404        } else {
405            write!(f, "⟨{}⟩", self.shape)
406        }
407    }
408}
409
410impl<'mem, 'facet, 'shape> core::cmp::PartialEq for Peek<'mem, 'facet, 'shape> {
411    fn eq(&self, other: &Self) -> bool {
412        if self.shape != other.shape {
413            return false;
414        }
415        let eq_fn = match (self.shape.vtable.partial_eq)() {
416            Some(eq_fn) => eq_fn,
417            None => return false,
418        };
419        unsafe { eq_fn(self.data, other.data) }
420    }
421}
422
423impl<'mem, 'facet, 'shape> core::cmp::PartialOrd for Peek<'mem, 'facet, 'shape> {
424    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
425        if self.shape != other.shape {
426            return None;
427        }
428        let partial_ord_fn = (self.shape.vtable.partial_ord)()?;
429        unsafe { partial_ord_fn(self.data, other.data) }
430    }
431}
432
433impl<'mem, 'facet, 'shape> core::hash::Hash for Peek<'mem, 'facet, 'shape> {
434    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
435        if let Some(hash_fn) = (self.shape.vtable.hash)() {
436            let hasher_opaque = PtrMut::new(hasher);
437            unsafe {
438                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
439                    opaque.as_mut::<H>().write(bytes)
440                })
441            };
442        } else {
443            panic!("Hashing is not supported for this shape");
444        }
445    }
446}