facet_reflect/peek/
value.rs

1use core::{cmp::Ordering, marker::PhantomData};
2use facet_core::{
3    Def, Facet, PointerType, PtrConst, PtrMut, SequenceType, Shape, Type, TypeNameOpts, UserType,
4    ValueVTable,
5};
6
7use crate::{ReflectError, ScalarType};
8
9use super::{
10    ListLikeDef, PeekEnum, PeekList, PeekListLike, PeekMap, PeekSmartPointer, PeekStruct, PeekTuple,
11};
12
13/// A unique identifier for a peek value
14#[derive(Clone, Copy, PartialEq, Eq, Hash)]
15pub struct ValueId<'shape> {
16    pub(crate) shape: &'shape Shape<'shape>,
17    pub(crate) ptr: *const u8,
18}
19
20impl<'shape> ValueId<'shape> {
21    pub(crate) fn new(shape: &'shape Shape<'shape>, ptr: *const u8) -> Self {
22        Self { shape, ptr }
23    }
24}
25
26impl core::fmt::Display for ValueId<'_> {
27    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28        write!(f, "{}@{:p}", self.shape, self.ptr)
29    }
30}
31
32impl core::fmt::Debug for ValueId<'_> {
33    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34        core::fmt::Display::fmt(self, f)
35    }
36}
37
38/// Lets you read from a value (implements read-only [`ValueVTable`] proxies)
39#[derive(Clone, Copy)]
40pub struct Peek<'mem, 'facet, 'shape> {
41    /// Underlying data
42    pub(crate) data: PtrConst<'mem>,
43
44    /// Shape of the value
45    pub(crate) shape: &'shape Shape<'shape>,
46
47    invariant: PhantomData<fn(&'facet ()) -> &'facet ()>,
48}
49
50impl<'mem, 'facet, 'shape> Peek<'mem, 'facet, 'shape> {
51    /// Creates a new `PeekValue` instance for a value of type `T`.
52    pub fn new<T: Facet<'facet>>(t: &'mem T) -> Self {
53        Self {
54            data: PtrConst::new(t as *const T),
55            shape: T::SHAPE,
56            invariant: PhantomData,
57        }
58    }
59
60    /// Creates a new `PeekValue` instance without checking the type.
61    ///
62    /// # Safety
63    ///
64    /// This function is unsafe because it doesn't check if the provided data
65    /// and shape are compatible. The caller must ensure that the data is valid
66    /// for the given shape.
67    pub unsafe fn unchecked_new(data: PtrConst<'mem>, shape: &'shape Shape<'shape>) -> Self {
68        Self {
69            data,
70            shape,
71            invariant: PhantomData,
72        }
73    }
74
75    /// Returns the vtable
76    #[inline(always)]
77    pub fn vtable(&self) -> &'shape ValueVTable {
78        self.shape.vtable
79    }
80
81    /// Returns a unique identifier for this value, usable for cycle detection
82    pub fn id(&self) -> ValueId<'shape> {
83        ValueId::new(self.shape, self.data.as_byte_ptr())
84    }
85
86    /// Returns true if the two values are pointer-equal
87    #[inline]
88    pub fn ptr_eq(&self, other: &Peek<'_, '_, '_>) -> bool {
89        self.data.as_byte_ptr() == other.data.as_byte_ptr()
90    }
91
92    /// Returns true if this scalar is equal to the other scalar
93    ///
94    /// # Returns
95    ///
96    /// `false` if equality comparison is not supported for this scalar type
97    #[inline]
98    pub fn partial_eq(&self, other: &Peek<'_, '_, '_>) -> Option<bool> {
99        unsafe { (self.shape.vtable.partial_eq)().map(|eq_fn| eq_fn(self.data, other.data)) }
100    }
101
102    /// Compares this scalar with another and returns their ordering
103    ///
104    /// # Returns
105    ///
106    /// `None` if comparison is not supported for this scalar type
107    #[inline]
108    pub fn partial_cmp(&self, other: &Peek<'_, '_, '_>) -> Option<Ordering> {
109        unsafe {
110            (self.shape.vtable.partial_ord)()
111                .and_then(|partial_ord_fn| partial_ord_fn(self.data, other.data))
112        }
113    }
114
115    /// Hashes this scalar
116    ///
117    /// # Returns
118    ///
119    /// `false` if hashing is not supported for this scalar type, `true` otherwise
120    #[inline(always)]
121    pub fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) -> bool {
122        unsafe {
123            if let Some(hash_fn) = (self.shape.vtable.hash)() {
124                let hasher_opaque = PtrMut::new(hasher);
125                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
126                    opaque.as_mut::<H>().write(bytes)
127                });
128                true
129            } else {
130                false
131            }
132        }
133    }
134
135    /// Returns the type name of this scalar
136    ///
137    /// # Arguments
138    ///
139    /// * `f` - A mutable reference to a `core::fmt::Formatter`
140    /// * `opts` - The `TypeNameOpts` to use for formatting
141    ///
142    /// # Returns
143    ///
144    /// The result of the type name formatting
145    #[inline(always)]
146    pub fn type_name(
147        &self,
148        f: &mut core::fmt::Formatter<'_>,
149        opts: TypeNameOpts,
150    ) -> core::fmt::Result {
151        (self.shape.vtable.type_name)(f, opts)
152    }
153
154    /// Returns the shape
155    #[inline(always)]
156    pub const fn shape(&self) -> &'shape Shape<'shape> {
157        self.shape
158    }
159
160    /// Returns the data
161    #[inline(always)]
162    pub const fn data(&self) -> PtrConst<'mem> {
163        self.data
164    }
165
166    /// Get the scalar type if set.
167    pub fn scalar_type(&self) -> Option<ScalarType> {
168        ScalarType::try_from_shape(self.shape)
169    }
170
171    /// Read the value from memory into a Rust value.
172    ///
173    /// # Panics
174    ///
175    /// Panics if the shape doesn't match the type `T`.
176    pub fn get<T: Facet<'facet>>(&self) -> Result<&T, ReflectError<'shape>> {
177        if self.shape != T::SHAPE {
178            Err(ReflectError::WrongShape {
179                expected: self.shape,
180                actual: T::SHAPE,
181            })
182        } else {
183            Ok(unsafe { self.data.get::<T>() })
184        }
185    }
186
187    /// Try to get the value as a string if it's a string type
188    /// Returns None if the value is not a string or couldn't be extracted
189    pub fn as_str(&self) -> Option<&'mem str> {
190        let peek = self.innermost_peek();
191        if let Some(ScalarType::Str) = peek.scalar_type() {
192            unsafe { Some(peek.data.get::<&str>()) }
193        } else if let Some(ScalarType::String) = peek.scalar_type() {
194            unsafe { Some(peek.data.get::<alloc::string::String>().as_str()) }
195        } else if let Type::Pointer(PointerType::Reference(vpt)) = peek.shape.ty {
196            let target_shape = (vpt.target)();
197            if let Some(ScalarType::Str) = ScalarType::try_from_shape(target_shape) {
198                unsafe { Some(peek.data.get::<&str>()) }
199            } else {
200                None
201            }
202        } else {
203            None
204        }
205    }
206
207    /// Tries to identify this value as a struct
208    pub fn into_struct(self) -> Result<PeekStruct<'mem, 'facet, 'shape>, ReflectError<'shape>> {
209        if let Type::User(UserType::Struct(ty)) = self.shape.ty {
210            Ok(PeekStruct { value: self, ty })
211        } else {
212            Err(ReflectError::WasNotA {
213                expected: "struct",
214                actual: self.shape,
215            })
216        }
217    }
218
219    /// Tries to identify this value as an enum
220    pub fn into_enum(self) -> Result<PeekEnum<'mem, 'facet, 'shape>, ReflectError<'shape>> {
221        if let Type::User(UserType::Enum(ty)) = self.shape.ty {
222            Ok(PeekEnum { value: self, ty })
223        } else {
224            Err(ReflectError::WasNotA {
225                expected: "enum",
226                actual: self.shape,
227            })
228        }
229    }
230
231    /// Tries to identify this value as a map
232    pub fn into_map(self) -> Result<PeekMap<'mem, 'facet, 'shape>, ReflectError<'shape>> {
233        if let Def::Map(def) = self.shape.def {
234            Ok(PeekMap { value: self, def })
235        } else {
236            Err(ReflectError::WasNotA {
237                expected: "map",
238                actual: self.shape,
239            })
240        }
241    }
242
243    /// Tries to identify this value as a list
244    pub fn into_list(self) -> Result<PeekList<'mem, 'facet, 'shape>, ReflectError<'shape>> {
245        if let Def::List(def) = self.shape.def {
246            return Ok(PeekList { value: self, def });
247        }
248
249        Err(ReflectError::WasNotA {
250            expected: "list",
251            actual: self.shape,
252        })
253    }
254
255    /// Tries to identify this value as a list, array or slice
256    pub fn into_list_like(
257        self,
258    ) -> Result<PeekListLike<'mem, 'facet, 'shape>, ReflectError<'shape>> {
259        match self.shape.def {
260            Def::List(def) => Ok(PeekListLike::new(self, ListLikeDef::List(def))),
261            Def::Array(def) => Ok(PeekListLike::new(self, ListLikeDef::Array(def))),
262            _ => {
263                // &[i32] is actually a _pointer_ to a slice.
264                match self.shape.ty {
265                    Type::Pointer(ptr) => match ptr {
266                        PointerType::Reference(vpt) | PointerType::Raw(vpt) => {
267                            let target = (vpt.target)();
268                            match target.def {
269                                Def::Slice(def) => {
270                                    return Ok(PeekListLike::new(self, ListLikeDef::Slice(def)));
271                                }
272                                _ => {
273                                    // well it's not list-like then
274                                }
275                            }
276                        }
277                        PointerType::Function(_) => {
278                            // well that's not a list-like
279                        }
280                    },
281                    _ => {
282                        // well that's not a list-like either
283                    }
284                }
285
286                Err(ReflectError::WasNotA {
287                    expected: "list, array or slice",
288                    actual: self.shape,
289                })
290            }
291        }
292    }
293
294    /// Tries to identify this value as a smart pointer
295    pub fn into_smart_pointer(
296        self,
297    ) -> Result<PeekSmartPointer<'mem, 'facet, 'shape>, ReflectError<'shape>> {
298        if let Def::SmartPointer(def) = self.shape.def {
299            Ok(PeekSmartPointer { value: self, def })
300        } else {
301            Err(ReflectError::WasNotA {
302                expected: "smart pointer",
303                actual: self.shape,
304            })
305        }
306    }
307
308    /// Tries to identify this value as an option
309    pub fn into_option(
310        self,
311    ) -> Result<super::PeekOption<'mem, 'facet, 'shape>, ReflectError<'shape>> {
312        if let Def::Option(def) = self.shape.def {
313            Ok(super::PeekOption { value: self, def })
314        } else {
315            Err(ReflectError::WasNotA {
316                expected: "option",
317                actual: self.shape,
318            })
319        }
320    }
321
322    /// Tries to identify this value as a tuple
323    pub fn into_tuple(self) -> Result<PeekTuple<'mem, 'facet, 'shape>, ReflectError<'shape>> {
324        if let Type::Sequence(SequenceType::Tuple(ty)) = self.shape.ty {
325            Ok(PeekTuple { value: self, ty })
326        } else {
327            Err(ReflectError::WasNotA {
328                expected: "tuple",
329                actual: self.shape,
330            })
331        }
332    }
333
334    /// Tries to return the innermost value — useful for serialization. For example, we serialize a `NonZero<u8>` the same
335    /// as a `u8`. Similarly, we serialize a `Utf8PathBuf` the same as a `String.
336    ///
337    /// Returns a `Peek` to the innermost value, unwrapping transparent wrappers recursively.
338    /// For example, this will peel through newtype wrappers or smart pointers that have an `inner`.
339    pub fn innermost_peek(self) -> Self {
340        let mut current_peek = self;
341        while let (Some(try_borrow_inner_fn), Some(inner_shape)) = (
342            (current_peek.shape.vtable.try_borrow_inner)(),
343            current_peek.shape.inner,
344        ) {
345            unsafe {
346                let inner_data = try_borrow_inner_fn(current_peek.data).unwrap_or_else(|e| {
347                    panic!("innermost_peek: try_borrow_inner returned an error! was trying to go from {} to {}. error: {e}", current_peek.shape,
348                        inner_shape())
349                });
350
351                current_peek = Peek {
352                    data: inner_data,
353                    shape: inner_shape(),
354                    invariant: PhantomData,
355                };
356            }
357        }
358        current_peek
359    }
360}
361
362impl<'mem, 'facet, 'shape> core::fmt::Display for Peek<'mem, 'facet, 'shape> {
363    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
364        if let Some(display_fn) = (self.vtable().display)() {
365            unsafe { display_fn(self.data, f) }
366        } else {
367            write!(f, "⟨{}⟩", self.shape)
368        }
369    }
370}
371
372impl<'mem, 'facet, 'shape> core::fmt::Debug for Peek<'mem, 'facet, 'shape> {
373    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
374        if let Some(debug_fn) = (self.vtable().debug)() {
375            unsafe { debug_fn(self.data, f) }
376        } else {
377            write!(f, "⟨{}⟩", self.shape)
378        }
379    }
380}
381
382impl<'mem, 'facet, 'shape> core::cmp::PartialEq for Peek<'mem, 'facet, 'shape> {
383    fn eq(&self, other: &Self) -> bool {
384        if self.shape != other.shape {
385            return false;
386        }
387        let eq_fn = match (self.shape.vtable.partial_eq)() {
388            Some(eq_fn) => eq_fn,
389            None => return false,
390        };
391        unsafe { eq_fn(self.data, other.data) }
392    }
393}
394
395impl<'mem, 'facet, 'shape> core::cmp::PartialOrd for Peek<'mem, 'facet, 'shape> {
396    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
397        if self.shape != other.shape {
398            return None;
399        }
400        let partial_ord_fn = (self.shape.vtable.partial_ord)()?;
401        unsafe { partial_ord_fn(self.data, other.data) }
402    }
403}
404
405impl<'mem, 'facet, 'shape> core::hash::Hash for Peek<'mem, 'facet, 'shape> {
406    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
407        if let Some(hash_fn) = (self.shape.vtable.hash)() {
408            let hasher_opaque = PtrMut::new(hasher);
409            unsafe {
410                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
411                    opaque.as_mut::<H>().write(bytes)
412                })
413            };
414        } else {
415            panic!("Hashing is not supported for this shape");
416        }
417    }
418}