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