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::{PeekNdArray, 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 ndarray
328    #[inline]
329    pub fn into_ndarray(self) -> Result<PeekNdArray<'mem, 'facet>, ReflectError> {
330        if let Def::NdArray(def) = self.shape.def {
331            return Ok(PeekNdArray { value: self, def });
332        }
333
334        Err(ReflectError::WasNotA {
335            expected: "ndarray",
336            actual: self.shape,
337        })
338    }
339
340    /// Tries to identify this value as a list, array or slice
341    #[inline]
342    pub fn into_list_like(self) -> Result<PeekListLike<'mem, 'facet>, ReflectError> {
343        match self.shape.def {
344            Def::List(def) => Ok(PeekListLike::new(self, ListLikeDef::List(def))),
345            Def::Array(def) => Ok(PeekListLike::new(self, ListLikeDef::Array(def))),
346            Def::Slice(def) => {
347                // When we have a bare slice shape with a wide pointer,
348                // it means we have a reference to a slice (e.g., from Arc<[T]>::borrow_inner)
349                if matches!(self.data, GenericPtr::Wide(_)) {
350                    Ok(PeekListLike::new(self, ListLikeDef::Slice(def)))
351                } else {
352                    Err(ReflectError::WasNotA {
353                        expected: "slice with wide pointer",
354                        actual: self.shape,
355                    })
356                }
357            }
358            _ => {
359                // &[i32] is actually a _pointer_ to a slice.
360                match self.shape.ty {
361                    Type::Pointer(ptr) => match ptr {
362                        PointerType::Reference(vpt) | PointerType::Raw(vpt) => {
363                            let target = (vpt.target)();
364                            match target.def {
365                                Def::Slice(def) => {
366                                    return Ok(PeekListLike::new(self, ListLikeDef::Slice(def)));
367                                }
368                                _ => {
369                                    // well it's not list-like then
370                                }
371                            }
372                        }
373                        PointerType::Function(_) => {
374                            // well that's not a list-like
375                        }
376                    },
377                    _ => {
378                        // well that's not a list-like either
379                    }
380                }
381
382                Err(ReflectError::WasNotA {
383                    expected: "list, array or slice",
384                    actual: self.shape,
385                })
386            }
387        }
388    }
389
390    /// Tries to identify this value as a pointer
391    #[inline]
392    pub fn into_pointer(self) -> Result<PeekPointer<'mem, 'facet>, ReflectError> {
393        if let Def::Pointer(def) = self.shape.def {
394            Ok(PeekPointer { value: self, def })
395        } else {
396            Err(ReflectError::WasNotA {
397                expected: "smart pointer",
398                actual: self.shape,
399            })
400        }
401    }
402
403    /// Tries to identify this value as an option
404    #[inline]
405    pub fn into_option(self) -> Result<PeekOption<'mem, 'facet>, ReflectError> {
406        if let Def::Option(def) = self.shape.def {
407            Ok(PeekOption { value: self, def })
408        } else {
409            Err(ReflectError::WasNotA {
410                expected: "option",
411                actual: self.shape,
412            })
413        }
414    }
415
416    /// Tries to identify this value as a tuple
417    #[inline]
418    pub fn into_tuple(self) -> Result<PeekTuple<'mem, 'facet>, ReflectError> {
419        if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
420            if struct_type.kind == StructKind::Tuple {
421                Ok(PeekTuple {
422                    value: self,
423                    ty: TupleType {
424                        fields: struct_type.fields,
425                    },
426                })
427            } else {
428                Err(ReflectError::WasNotA {
429                    expected: "tuple",
430                    actual: self.shape,
431                })
432            }
433        } else {
434            Err(ReflectError::WasNotA {
435                expected: "tuple",
436                actual: self.shape,
437            })
438        }
439    }
440
441    /// Tries to return the innermost value — useful for serialization. For example, we serialize a `NonZero<u8>` the same
442    /// as a `u8`. Similarly, we serialize a `Utf8PathBuf` the same as a `String.
443    ///
444    /// Returns a `Peek` to the innermost value, unwrapping transparent wrappers recursively.
445    /// For example, this will peel through newtype wrappers or smart pointers that have an `inner`.
446    pub fn innermost_peek(self) -> Self {
447        let mut current_peek = self;
448        while let (Some(try_borrow_inner_fn), Some(inner_shape)) = (
449            current_peek
450                .shape
451                .vtable
452                .sized()
453                .and_then(|v| (v.try_borrow_inner)()),
454            current_peek.shape.inner,
455        ) {
456            unsafe {
457                let inner_data = try_borrow_inner_fn(current_peek.data.thin().unwrap()).unwrap_or_else(|e| {
458                    panic!("innermost_peek: try_borrow_inner returned an error! was trying to go from {} to {}. error: {e}", current_peek.shape,
459                        inner_shape())
460                });
461
462                current_peek = Peek {
463                    data: inner_data.into(),
464                    shape: inner_shape(),
465                    invariant: PhantomData,
466                };
467            }
468        }
469        current_peek
470    }
471}
472
473impl<'mem, 'facet> core::fmt::Display for Peek<'mem, 'facet> {
474    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
475        match self.data {
476            GenericPtr::Thin(ptr) => {
477                if let Some(display_fn) = (self.vtable().sized().unwrap().display)() {
478                    return unsafe { display_fn(ptr, f) };
479                }
480            }
481            GenericPtr::Wide(ptr) => {
482                if let Some(display_fn) = (self.vtable().r#unsized().unwrap().display)() {
483                    return unsafe { display_fn(ptr, f) };
484                }
485            }
486        }
487        write!(f, "⟨{}⟩", self.shape)
488    }
489}
490
491impl<'mem, 'facet> core::fmt::Debug for Peek<'mem, 'facet> {
492    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
493        match self.data {
494            GenericPtr::Thin(ptr) => {
495                if let Some(debug_fn) = (self.vtable().sized().unwrap().debug)() {
496                    return unsafe { debug_fn(ptr, f) };
497                }
498            }
499            GenericPtr::Wide(ptr) => {
500                if let Some(debug_fn) = (self.vtable().r#unsized().unwrap().debug)() {
501                    return unsafe { debug_fn(ptr, f) };
502                }
503            }
504        }
505        write!(f, "⟨{}⟩", self.shape)
506    }
507}
508
509impl<'mem, 'facet> core::cmp::PartialEq for Peek<'mem, 'facet> {
510    #[inline]
511    fn eq(&self, other: &Self) -> bool {
512        self.partial_eq(other).unwrap_or(false)
513    }
514}
515
516impl<'mem, 'facet> core::cmp::PartialOrd for Peek<'mem, 'facet> {
517    #[inline]
518    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
519        self.partial_cmp(other).unwrap_or(None)
520    }
521}
522
523impl<'mem, 'facet> core::hash::Hash for Peek<'mem, 'facet> {
524    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
525        self.hash(hasher)
526            .expect("Hashing is not supported for this shape");
527    }
528}