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