facet_reflect/peek/
value.rs

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