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