facet_reflect/peek/
value.rs

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