facet_reflect/peek/
value.rs

1use core::{cmp::Ordering, marker::PhantomData, ptr::NonNull};
2#[cfg(feature = "alloc")]
3use facet_core::Field;
4use facet_core::{
5    Def, Facet, PointerType, PtrConst, Shape, StructKind, Type, TypeNameOpts, UserType,
6    VTableErased, Variance,
7};
8
9use crate::{PeekNdArray, PeekSet, ReflectError, ScalarType};
10
11use super::{
12    ListLikeDef, PeekDynamicValue, PeekEnum, PeekList, PeekListLike, PeekMap, PeekOption,
13    PeekPointer, PeekResult, PeekStruct, PeekTuple, tuple::TupleType,
14};
15
16#[cfg(feature = "alloc")]
17use super::OwnedPeek;
18
19/// A unique identifier for a peek value
20#[derive(Clone, Copy, PartialEq, PartialOrd, Ord, Eq, Hash)]
21pub struct ValueId {
22    pub(crate) shape: &'static Shape,
23    pub(crate) ptr: *const u8,
24}
25
26impl ValueId {
27    #[inline]
28    pub(crate) fn new(shape: &'static Shape, ptr: *const u8) -> Self {
29        Self { shape, ptr }
30    }
31}
32
33impl core::fmt::Display for ValueId {
34    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
35        write!(f, "{}@{:p}", self.shape, self.ptr)
36    }
37}
38
39impl core::fmt::Debug for ValueId {
40    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
41        core::fmt::Display::fmt(self, f)
42    }
43}
44
45/// A read-only view into a value with runtime type information.
46///
47/// `Peek` provides reflection capabilities for reading values at runtime.
48/// If the value is a struct, you can read its fields; if it's an enum,
49/// you can determine which variant is selected; if it's a scalar, you can
50/// extract a concrete value.
51///
52/// # Lifetime Parameters
53///
54/// - `'mem`: The memory lifetime - how long the underlying data is valid
55/// - `'facet`: The type's lifetime parameter (for types like `&'a str`)
56///
57/// # Variance and Soundness
58///
59/// `Peek` is **invariant** over `'facet`. This is required for soundness:
60/// if `Peek` were covariant, it would be possible to launder lifetimes
61/// through reflection, leading to use-after-free bugs with types like
62/// `fn(&'a str)`. See [issue #1168](https://github.com/facet-rs/facet/issues/1168).
63///
64/// The underlying type's variance is tracked in [`Shape::variance`], which
65/// can be used for future variance-aware APIs.
66#[allow(clippy::type_complexity)]
67#[derive(Clone, Copy)]
68pub struct Peek<'mem, 'facet> {
69    /// Underlying data
70    pub(crate) data: PtrConst,
71
72    /// Shape of the value
73    pub(crate) shape: &'static Shape,
74
75    // Invariant over 'facet: Peek<'mem, 'a> cannot be cast to Peek<'mem, 'b> even if 'a: 'b.
76    //
77    // This is REQUIRED for soundness! If Peek were covariant over 'facet, we could:
78    // 1. Create Peek<'mem, 'static> from FnWrapper<'static> (contains fn(&'static str))
79    // 2. Use covariance to cast it to Peek<'mem, 'short>
80    // 3. Call get::<FnWrapper<'short>>() to get &FnWrapper<'short>
81    // 4. This would allow calling the function with a &'short str that goes out of scope
82    //    while the original function pointer still holds it as 'static
83    //
84    // The fn(&'a ()) -> &'a () pattern makes this type invariant over 'facet.
85    // The &'mem () makes this type covariant over 'mem (safe because we only read through it).
86    // See: https://github.com/facet-rs/facet/issues/1168
87    _invariant: PhantomData<(&'mem (), fn(&'facet ()) -> &'facet ())>,
88}
89
90impl<'mem, 'facet> Peek<'mem, 'facet> {
91    /// Returns a read-only view over a `T` value.
92    pub fn new<T: Facet<'facet> + ?Sized>(t: &'mem T) -> Self {
93        Self {
94            data: PtrConst::new(NonNull::from(t).as_ptr()),
95            shape: T::SHAPE,
96            _invariant: PhantomData,
97        }
98    }
99
100    /// Returns a read-only view over a value (given its shape), trusting you
101    /// that those two match.
102    ///
103    /// # Safety
104    ///
105    /// This function is unsafe because it doesn't check if the provided data
106    /// and shape are compatible. The caller must ensure that the data is valid
107    /// for the given shape.
108    pub unsafe fn unchecked_new(data: PtrConst, shape: &'static Shape) -> Self {
109        Self {
110            data,
111            shape,
112            _invariant: PhantomData,
113        }
114    }
115
116    // =============================================================================
117    // Variance-aware lifetime transformation methods
118    // =============================================================================
119
120    /// Returns the computed variance of the underlying type.
121    ///
122    /// This walks the type's fields to determine if the type is covariant,
123    /// contravariant, or invariant over its lifetime parameter.
124    #[inline]
125    pub fn variance(&self) -> Variance {
126        self.shape.computed_variance()
127    }
128
129    /// Shrinks the `'facet` lifetime parameter.
130    ///
131    /// This is safe for covariant types: if data is valid for `'static`,
132    /// it's also valid for any shorter lifetime `'shorter`.
133    ///
134    /// # Panics
135    ///
136    /// Panics if the type is not covariant (i.e., if shrinking would be unsound).
137    #[inline]
138    pub fn shrink_lifetime<'shorter>(self) -> Peek<'mem, 'shorter>
139    where
140        'facet: 'shorter,
141    {
142        self.try_shrink_lifetime()
143            .expect("shrink_lifetime requires a covariant type")
144    }
145
146    /// Tries to shrink the `'facet` lifetime parameter.
147    ///
148    /// Returns `Some` if the type is covariant (shrinking is safe),
149    /// or `None` if the type is invariant or contravariant.
150    #[inline]
151    pub fn try_shrink_lifetime<'shorter>(self) -> Option<Peek<'mem, 'shorter>>
152    where
153        'facet: 'shorter,
154    {
155        if self.variance() == Variance::Covariant {
156            Some(Peek {
157                data: self.data,
158                shape: self.shape,
159                _invariant: PhantomData,
160            })
161        } else {
162            None
163        }
164    }
165
166    /// Grows the `'facet` lifetime parameter.
167    ///
168    /// This is safe for contravariant types: if a function accepts `'short`,
169    /// it can also accept `'longer` (a longer lifetime is more restrictive).
170    ///
171    /// # Panics
172    ///
173    /// Panics if the type is not contravariant (i.e., if growing would be unsound).
174    #[inline]
175    pub fn grow_lifetime<'longer>(self) -> Peek<'mem, 'longer>
176    where
177        'longer: 'facet,
178    {
179        self.try_grow_lifetime()
180            .expect("grow_lifetime requires a contravariant type")
181    }
182
183    /// Tries to grow the `'facet` lifetime parameter.
184    ///
185    /// Returns `Some` if the type is contravariant (growing is safe),
186    /// or `None` if the type is invariant or covariant.
187    #[inline]
188    pub fn try_grow_lifetime<'longer>(self) -> Option<Peek<'mem, 'longer>>
189    where
190        'longer: 'facet,
191    {
192        if self.variance() == Variance::Contravariant {
193            Some(Peek {
194                data: self.data,
195                shape: self.shape,
196                _invariant: PhantomData,
197            })
198        } else {
199            None
200        }
201    }
202
203    /// Returns the vtable
204    #[inline(always)]
205    pub fn vtable(&self) -> VTableErased {
206        self.shape.vtable
207    }
208
209    /// Returns a unique identifier for this value, usable for cycle detection
210    #[inline]
211    pub fn id(&self) -> ValueId {
212        ValueId::new(self.shape, self.data.raw_ptr())
213    }
214
215    /// Returns true if the two values are pointer-equal
216    #[inline]
217    pub fn ptr_eq(&self, other: &Peek<'_, '_>) -> bool {
218        self.data.raw_ptr() == other.data.raw_ptr()
219    }
220
221    /// Returns true if this scalar is equal to the other scalar
222    ///
223    /// # Returns
224    ///
225    /// `false` if equality comparison is not supported for this scalar type
226    #[inline]
227    pub fn partial_eq(&self, other: &Peek<'_, '_>) -> Result<bool, ReflectError> {
228        if self.shape != other.shape {
229            return Err(ReflectError::WrongShape {
230                expected: self.shape,
231                actual: other.shape,
232            });
233        }
234
235        if let Some(result) = unsafe { self.shape.call_partial_eq(self.data, other.data) } {
236            return Ok(result);
237        }
238
239        Err(ReflectError::OperationFailed {
240            shape: self.shape(),
241            operation: "partial_eq",
242        })
243    }
244
245    /// Compares this scalar with another and returns their ordering
246    ///
247    /// # Returns
248    ///
249    /// `None` if comparison is not supported for this scalar type
250    #[inline]
251    pub fn partial_cmp(&self, other: &Peek<'_, '_>) -> Result<Option<Ordering>, ReflectError> {
252        if self.shape != other.shape {
253            return Err(ReflectError::WrongShape {
254                expected: self.shape,
255                actual: other.shape,
256            });
257        }
258
259        if let Some(result) = unsafe { self.shape.call_partial_cmp(self.data, other.data) } {
260            return Ok(result);
261        }
262
263        Err(ReflectError::OperationFailed {
264            shape: self.shape(),
265            operation: "partial_cmp",
266        })
267    }
268
269    /// Hashes this scalar using the vtable hash function.
270    ///
271    /// # Returns
272    ///
273    /// `Err` if hashing is not supported for this scalar type, `Ok` otherwise
274    #[inline(always)]
275    pub fn hash(&self, hasher: &mut dyn core::hash::Hasher) -> Result<(), ReflectError> {
276        let mut proxy = facet_core::HashProxy::new(hasher);
277        if unsafe { self.shape.call_hash(self.data, &mut proxy) }.is_some() {
278            return Ok(());
279        }
280
281        Err(ReflectError::OperationFailed {
282            shape: self.shape(),
283            operation: "hash",
284        })
285    }
286
287    /// Computes a structural hash of this value.
288    ///
289    /// Unlike [`hash`](Self::hash), this method recursively traverses the structure
290    /// and hashes each component, making it work for types that don't implement `Hash`.
291    ///
292    /// For scalars with a vtable hash function, it uses that. For compound types
293    /// (structs, enums, lists, etc.), it recursively hashes the structure.
294    ///
295    /// This is useful for Merkle-tree style hashing where you want to compare
296    /// subtrees for equality based on their structural content.
297    pub fn structural_hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
298        use core::hash::Hash;
299
300        // First, hash the shape's type identifier for type discrimination
301        self.shape.id.hash(hasher);
302
303        // Try vtable hash first for scalars
304        let mut proxy = facet_core::HashProxy::new(hasher);
305        if unsafe { self.shape.call_hash(self.data, &mut proxy) }.is_some() {
306            return;
307        }
308
309        // Otherwise, traverse the structure recursively
310        match self.shape.ty {
311            Type::User(UserType::Struct(struct_type)) => {
312                // Hash struct kind
313                (struct_type.kind as u8).hash(hasher);
314
315                // Hash each field, skipping metadata fields
316                for field in struct_type.fields {
317                    // Skip metadata fields - they don't affect structural identity
318                    if field.is_metadata() {
319                        continue;
320                    }
321
322                    // Hash field name
323                    field.name.hash(hasher);
324
325                    // Get field value and hash it recursively
326                    let field_offset = field.offset;
327                    let field_shape = field.shape();
328                    let field_ptr = unsafe { self.data.field(field_offset) };
329                    let field_peek = unsafe { Peek::unchecked_new(field_ptr, field_shape) };
330                    field_peek.structural_hash(hasher);
331                }
332            }
333
334            Type::User(UserType::Enum(_enum_type)) => {
335                // Get the discriminant and variant
336                if let Ok(peek_enum) = self.into_enum()
337                    && let Ok(variant) = peek_enum.active_variant()
338                {
339                    // Hash variant name
340                    variant.name.hash(hasher);
341
342                    // Hash variant payload based on kind
343                    match variant.data.kind {
344                        StructKind::Unit => {
345                            // No payload to hash
346                        }
347                        StructKind::TupleStruct | StructKind::Tuple => {
348                            // Hash tuple fields (no names)
349                            use super::HasFields;
350                            for (_field, peek) in peek_enum.fields() {
351                                peek.structural_hash(hasher);
352                            }
353                        }
354                        StructKind::Struct => {
355                            // Hash named fields
356                            use super::HasFields;
357                            for (field, peek) in peek_enum.fields() {
358                                field.name.hash(hasher);
359                                peek.structural_hash(hasher);
360                            }
361                        }
362                    }
363                }
364            }
365
366            _ => {
367                // Handle Def-based types
368                match self.shape.def {
369                    Def::List(_) | Def::Array(_) | Def::Slice(_) => {
370                        if let Ok(list_like) = self.into_list_like() {
371                            // Hash length
372                            list_like.len().hash(hasher);
373
374                            // Hash each element
375                            for elem in list_like.iter() {
376                                elem.structural_hash(hasher);
377                            }
378                        }
379                    }
380
381                    Def::Map(_) => {
382                        if let Ok(map) = self.into_map() {
383                            // Hash length
384                            map.len().hash(hasher);
385
386                            // Hash each key-value pair
387                            for (key, value) in map.iter() {
388                                key.structural_hash(hasher);
389                                value.structural_hash(hasher);
390                            }
391                        }
392                    }
393
394                    Def::Set(_) => {
395                        if let Ok(set) = self.into_set() {
396                            // Hash length
397                            set.len().hash(hasher);
398
399                            // Hash each element
400                            for elem in set.iter() {
401                                elem.structural_hash(hasher);
402                            }
403                        }
404                    }
405
406                    Def::Option(_) => {
407                        if let Ok(opt) = self.into_option() {
408                            if let Some(inner) = opt.value() {
409                                true.hash(hasher);
410                                inner.structural_hash(hasher);
411                            } else {
412                                false.hash(hasher);
413                            }
414                        }
415                    }
416
417                    Def::Result(_) => {
418                        if let Ok(result) = self.into_result() {
419                            if result.is_ok() {
420                                0u8.hash(hasher);
421                                if let Some(ok_val) = result.ok() {
422                                    ok_val.structural_hash(hasher);
423                                }
424                            } else {
425                                1u8.hash(hasher);
426                                if let Some(err_val) = result.err() {
427                                    err_val.structural_hash(hasher);
428                                }
429                            }
430                        }
431                    }
432
433                    Def::Pointer(_) => {
434                        if let Ok(ptr) = self.into_pointer()
435                            && let Some(inner) = ptr.borrow_inner()
436                        {
437                            inner.structural_hash(hasher);
438                        }
439                    }
440
441                    Def::DynamicValue(_) => {
442                        if let Ok(dyn_val) = self.into_dynamic_value() {
443                            // Hash based on dynamic value kind
444                            dyn_val.structural_hash_inner(hasher);
445                        }
446                    }
447
448                    Def::NdArray(_) => {
449                        // For ndarray, hash the dimensions and data
450                        if let Ok(arr) = self.into_ndarray() {
451                            let n_dim = arr.n_dim();
452                            n_dim.hash(hasher);
453                            for i in 0..n_dim {
454                                if let Some(dim) = arr.dim(i) {
455                                    dim.hash(hasher);
456                                }
457                            }
458                            // Hash each element
459                            let count = arr.count();
460                            for i in 0..count {
461                                if let Some(elem) = arr.get(i) {
462                                    elem.structural_hash(hasher);
463                                }
464                            }
465                        }
466                    }
467
468                    Def::Scalar | Def::Undefined | _ => {
469                        // Try to handle f32/f64 by hashing their bit representation
470                        match self.scalar_type() {
471                            Some(ScalarType::F32) => {
472                                if let Ok(v) = self.get::<f32>() {
473                                    v.to_bits().hash(hasher);
474                                    return;
475                                }
476                            }
477                            Some(ScalarType::F64) => {
478                                if let Ok(v) = self.get::<f64>() {
479                                    v.to_bits().hash(hasher);
480                                    return;
481                                }
482                            }
483                            _ => {}
484                        }
485                        panic!(
486                            "structural_hash: type {} has no Hash impl and cannot be structurally hashed",
487                            self.shape
488                        );
489                    }
490                }
491            }
492        }
493    }
494
495    /// Returns the type name of this scalar
496    ///
497    /// # Arguments
498    ///
499    /// * `f` - A mutable reference to a `core::fmt::Formatter`
500    /// * `opts` - The `TypeNameOpts` to use for formatting
501    ///
502    /// # Returns
503    ///
504    /// The result of the type name formatting
505    #[inline(always)]
506    pub fn type_name(
507        &self,
508        f: &mut core::fmt::Formatter<'_>,
509        opts: TypeNameOpts,
510    ) -> core::fmt::Result {
511        if let Some(type_name_fn) = self.shape.type_name {
512            type_name_fn(self.shape, f, opts)
513        } else {
514            write!(f, "{}", self.shape.type_identifier)
515        }
516    }
517
518    /// Returns the shape
519    #[inline(always)]
520    pub const fn shape(&self) -> &'static Shape {
521        self.shape
522    }
523
524    /// Returns the data
525    #[inline(always)]
526    pub const fn data(&self) -> PtrConst {
527        self.data
528    }
529
530    /// Get the scalar type if set.
531    #[inline]
532    pub fn scalar_type(&self) -> Option<ScalarType> {
533        ScalarType::try_from_shape(self.shape)
534    }
535
536    /// Read the value from memory into a Rust value.
537    ///
538    /// # Panics
539    ///
540    /// Panics if the shape doesn't match the type `T`.
541    #[inline]
542    pub fn get<T: Facet<'facet> + ?Sized>(&self) -> Result<&'mem T, ReflectError> {
543        if self.shape != T::SHAPE {
544            Err(ReflectError::WrongShape {
545                expected: self.shape,
546                actual: T::SHAPE,
547            })
548        } else {
549            Ok(unsafe { self.data.get::<T>() })
550        }
551    }
552
553    /// Try to get the value as a string if it's a string type
554    /// Returns None if the value is not a string or couldn't be extracted
555    pub fn as_str(&self) -> Option<&'mem str> {
556        let peek = self.innermost_peek();
557        // ScalarType::Str matches both bare `str` and `&str`.
558        // For bare `str` (not a pointer), data points to str bytes directly.
559        // For `&str`, let it fall through to the pointer handler below.
560        if let Some(ScalarType::Str) = peek.scalar_type()
561            && !matches!(peek.shape.ty, Type::Pointer(_))
562        {
563            // Bare `str`: data is a wide pointer to str bytes.
564            // get::<str>() creates a &str reference to that data.
565            return unsafe { Some(peek.data.get::<str>()) };
566        }
567        #[cfg(feature = "alloc")]
568        if let Some(ScalarType::String) = peek.scalar_type() {
569            return unsafe { Some(peek.data.get::<alloc::string::String>().as_str()) };
570        }
571        #[cfg(feature = "alloc")]
572        if let Some(ScalarType::CowStr) = peek.scalar_type() {
573            return unsafe { Some(peek.data.get::<alloc::borrow::Cow<'mem, str>>().as_ref()) };
574        }
575
576        // Handle references, including nested references like &&str
577        if let Type::Pointer(PointerType::Reference(vpt)) = peek.shape.ty {
578            let target_shape = vpt.target;
579
580            // Check if this is a nested reference (&&str) first
581            if let Type::Pointer(PointerType::Reference(inner_vpt)) = target_shape.ty {
582                let inner_target_shape = inner_vpt.target;
583                if let Some(ScalarType::Str) = ScalarType::try_from_shape(inner_target_shape) {
584                    // For &&str, we need to dereference twice.
585                    // Read the outer reference (8 bytes) as a pointer to &str, then dereference
586                    let outer_ptr: *const *const &str =
587                        unsafe { peek.data.as_ptr::<*const &str>() };
588                    let inner_ref: &str = unsafe { **outer_ptr };
589                    return Some(inner_ref);
590                }
591            } else if let Some(ScalarType::Str) = ScalarType::try_from_shape(target_shape)
592                && !matches!(target_shape.ty, Type::Pointer(_))
593            {
594                // Simple case: &str (but only if target is not a pointer itself)
595                return unsafe { Some(peek.data.get::<&str>()) };
596            }
597        }
598        None
599    }
600
601    /// Try to get the value as a byte slice if it's a &[u8] type
602    /// Returns None if the value is not a byte slice or couldn't be extracted
603    #[inline]
604    pub fn as_bytes(&self) -> Option<&'mem [u8]> {
605        // Check if it's a direct &[u8]
606        if let Type::Pointer(PointerType::Reference(vpt)) = self.shape.ty {
607            let target_shape = vpt.target;
608            if let Def::Slice(sd) = target_shape.def
609                && sd.t().is_type::<u8>()
610            {
611                unsafe { return Some(self.data.get::<&[u8]>()) }
612            }
613        }
614        None
615    }
616
617    /// Tries to identify this value as a struct
618    #[inline]
619    pub fn into_struct(self) -> Result<PeekStruct<'mem, 'facet>, ReflectError> {
620        if let Type::User(UserType::Struct(ty)) = self.shape.ty {
621            Ok(PeekStruct { value: self, ty })
622        } else {
623            Err(ReflectError::WasNotA {
624                expected: "struct",
625                actual: self.shape,
626            })
627        }
628    }
629
630    /// Tries to identify this value as an enum
631    #[inline]
632    pub fn into_enum(self) -> Result<PeekEnum<'mem, 'facet>, ReflectError> {
633        if let Type::User(UserType::Enum(ty)) = self.shape.ty {
634            Ok(PeekEnum { value: self, ty })
635        } else {
636            Err(ReflectError::WasNotA {
637                expected: "enum",
638                actual: self.shape,
639            })
640        }
641    }
642
643    /// Tries to identify this value as a map
644    #[inline]
645    pub fn into_map(self) -> Result<PeekMap<'mem, 'facet>, ReflectError> {
646        if let Def::Map(def) = self.shape.def {
647            Ok(PeekMap { value: self, def })
648        } else {
649            Err(ReflectError::WasNotA {
650                expected: "map",
651                actual: self.shape,
652            })
653        }
654    }
655
656    /// Tries to identify this value as a set
657    #[inline]
658    pub fn into_set(self) -> Result<PeekSet<'mem, 'facet>, ReflectError> {
659        if let Def::Set(def) = self.shape.def {
660            Ok(PeekSet { value: self, def })
661        } else {
662            Err(ReflectError::WasNotA {
663                expected: "set",
664                actual: self.shape,
665            })
666        }
667    }
668
669    /// Tries to identify this value as a list
670    #[inline]
671    pub fn into_list(self) -> Result<PeekList<'mem, 'facet>, ReflectError> {
672        if let Def::List(def) = self.shape.def {
673            return Ok(PeekList { value: self, def });
674        }
675
676        Err(ReflectError::WasNotA {
677            expected: "list",
678            actual: self.shape,
679        })
680    }
681
682    /// Tries to identify this value as a ndarray
683    #[inline]
684    pub fn into_ndarray(self) -> Result<PeekNdArray<'mem, 'facet>, ReflectError> {
685        if let Def::NdArray(def) = self.shape.def {
686            return Ok(PeekNdArray { value: self, def });
687        }
688
689        Err(ReflectError::WasNotA {
690            expected: "ndarray",
691            actual: self.shape,
692        })
693    }
694
695    /// Tries to identify this value as a list, array or slice
696    #[inline]
697    pub fn into_list_like(self) -> Result<PeekListLike<'mem, 'facet>, ReflectError> {
698        match self.shape.def {
699            Def::List(def) => Ok(PeekListLike::new(self, ListLikeDef::List(def))),
700            Def::Array(def) => Ok(PeekListLike::new(self, ListLikeDef::Array(def))),
701            Def::Slice(def) => {
702                // When we have a bare slice shape with a wide pointer,
703                // it means we have a reference to a slice (e.g., from Arc<[T]>::borrow_inner)
704                Ok(PeekListLike::new(self, ListLikeDef::Slice(def)))
705            }
706            _ => {
707                // &[i32] is actually a _pointer_ to a slice.
708                match self.shape.ty {
709                    Type::Pointer(ptr) => match ptr {
710                        PointerType::Reference(vpt) | PointerType::Raw(vpt) => {
711                            let target = vpt.target;
712                            match target.def {
713                                Def::Slice(def) => {
714                                    let ptr = unsafe { self.data.as_ptr::<*const [()]>() };
715                                    let ptr = PtrConst::new(unsafe {
716                                        NonNull::new_unchecked((*ptr) as *mut [()]).as_ptr()
717                                    });
718                                    let peek = unsafe { Peek::unchecked_new(ptr, def.t) };
719
720                                    return Ok(PeekListLike::new(peek, ListLikeDef::Slice(def)));
721                                }
722                                _ => {
723                                    // well it's not list-like then
724                                }
725                            }
726                        }
727                        PointerType::Function(_) => {
728                            // well that's not a list-like
729                        }
730                    },
731                    _ => {
732                        // well that's not a list-like either
733                    }
734                }
735
736                Err(ReflectError::WasNotA {
737                    expected: "list, array or slice",
738                    actual: self.shape,
739                })
740            }
741        }
742    }
743
744    /// Tries to identify this value as a pointer
745    #[inline]
746    pub fn into_pointer(self) -> Result<PeekPointer<'mem, 'facet>, ReflectError> {
747        if let Def::Pointer(def) = self.shape.def {
748            Ok(PeekPointer { value: self, def })
749        } else {
750            Err(ReflectError::WasNotA {
751                expected: "smart pointer",
752                actual: self.shape,
753            })
754        }
755    }
756
757    /// Tries to identify this value as an option
758    #[inline]
759    pub fn into_option(self) -> Result<PeekOption<'mem, 'facet>, ReflectError> {
760        if let Def::Option(def) = self.shape.def {
761            Ok(PeekOption { value: self, def })
762        } else {
763            Err(ReflectError::WasNotA {
764                expected: "option",
765                actual: self.shape,
766            })
767        }
768    }
769
770    /// Tries to identify this value as a result
771    #[inline]
772    pub fn into_result(self) -> Result<PeekResult<'mem, 'facet>, ReflectError> {
773        if let Def::Result(def) = self.shape.def {
774            Ok(PeekResult { value: self, def })
775        } else {
776            Err(ReflectError::WasNotA {
777                expected: "result",
778                actual: self.shape,
779            })
780        }
781    }
782
783    /// Tries to identify this value as a tuple
784    #[inline]
785    pub fn into_tuple(self) -> Result<PeekTuple<'mem, 'facet>, ReflectError> {
786        if let Type::User(UserType::Struct(struct_type)) = self.shape.ty {
787            if struct_type.kind == StructKind::Tuple {
788                Ok(PeekTuple {
789                    value: self,
790                    ty: TupleType {
791                        fields: struct_type.fields,
792                    },
793                })
794            } else {
795                Err(ReflectError::WasNotA {
796                    expected: "tuple",
797                    actual: self.shape,
798                })
799            }
800        } else {
801            Err(ReflectError::WasNotA {
802                expected: "tuple",
803                actual: self.shape,
804            })
805        }
806    }
807
808    /// Tries to identify this value as a dynamic value (like `facet_value::Value`)
809    #[inline]
810    pub fn into_dynamic_value(self) -> Result<PeekDynamicValue<'mem, 'facet>, ReflectError> {
811        if let Def::DynamicValue(def) = self.shape.def {
812            Ok(PeekDynamicValue { value: self, def })
813        } else {
814            Err(ReflectError::WasNotA {
815                expected: "dynamic value",
816                actual: self.shape,
817            })
818        }
819    }
820
821    /// Tries to return the innermost value — useful for serialization. For example, we serialize a `NonZero<u8>` the same
822    /// as a `u8`. Similarly, we serialize a `Utf8PathBuf` the same as a `String.
823    ///
824    /// Returns a `Peek` to the innermost value, unwrapping transparent wrappers recursively.
825    /// For example, this will peel through newtype wrappers or smart pointers that have an `inner`.
826    pub fn innermost_peek(self) -> Self {
827        let mut current_peek = self;
828        while let Some(inner_shape) = current_peek.shape.inner {
829            // Try to borrow the inner value
830            let result = unsafe { current_peek.shape.call_try_borrow_inner(current_peek.data) };
831            match result {
832                Some(Ok(inner_data)) => {
833                    current_peek = Peek {
834                        data: inner_data.as_const(),
835                        shape: inner_shape,
836                        _invariant: PhantomData,
837                    };
838                }
839                Some(Err(e)) => {
840                    panic!(
841                        "innermost_peek: try_borrow_inner returned an error! was trying to go from {} to {}. error: {e}",
842                        current_peek.shape, inner_shape
843                    );
844                }
845                None => {
846                    // No try_borrow_inner function, stop here
847                    break;
848                }
849            }
850        }
851        current_peek
852    }
853
854    /// Performs custom serialization of the current peek using the provided field's metadata.
855    ///
856    /// Returns an `OwnedPeek` that points to the final type that should be serialized in place
857    /// of the current peek.
858    #[cfg(feature = "alloc")]
859    pub fn custom_serialization(&self, field: Field) -> Result<OwnedPeek<'mem>, ReflectError> {
860        let Some(proxy_def) = field.proxy() else {
861            return Err(ReflectError::OperationFailed {
862                shape: self.shape,
863                operation: "field does not have a proxy definition",
864            });
865        };
866
867        let target_shape = proxy_def.shape;
868        let tptr = target_shape.allocate().map_err(|_| ReflectError::Unsized {
869            shape: target_shape,
870            operation: "Not a Sized type",
871        })?;
872        let ser_res = unsafe { (proxy_def.convert_out)(self.data(), tptr) };
873        let err = match ser_res {
874            Ok(rptr) => {
875                if rptr.as_uninit() != tptr {
876                    ReflectError::CustomSerializationError {
877                        message: "convert_out did not return the expected pointer".into(),
878                        src_shape: self.shape,
879                        dst_shape: target_shape,
880                    }
881                } else {
882                    return Ok(OwnedPeek {
883                        shape: target_shape,
884                        data: rptr,
885                        _phantom: PhantomData,
886                    });
887                }
888            }
889            Err(message) => ReflectError::CustomSerializationError {
890                message,
891                src_shape: self.shape,
892                dst_shape: target_shape,
893            },
894        };
895        // if we reach here we have an error and we need to deallocate the target allocation
896        unsafe {
897            // SAFETY: unwrap should be ok since the allocation was ok
898            target_shape.deallocate_uninit(tptr).unwrap()
899        };
900        Err(err)
901    }
902
903    /// Returns an `OwnedPeek` using the shape's container-level proxy for serialization.
904    ///
905    /// This is used when a type has `#[facet(proxy = ProxyType)]` at the container level.
906    /// Unlike field-level proxies which are checked via `custom_serialization(field)`,
907    /// this method checks the Shape itself for a proxy definition.
908    ///
909    /// Returns `None` if the shape has no container-level proxy.
910    #[cfg(feature = "alloc")]
911    pub fn custom_serialization_from_shape(&self) -> Result<Option<OwnedPeek<'mem>>, ReflectError> {
912        let Some(proxy_def) = self.shape.proxy else {
913            return Ok(None);
914        };
915
916        let target_shape = proxy_def.shape;
917        let tptr = target_shape.allocate().map_err(|_| ReflectError::Unsized {
918            shape: target_shape,
919            operation: "Not a Sized type",
920        })?;
921
922        let ser_res = unsafe { (proxy_def.convert_out)(self.data(), tptr) };
923        let err = match ser_res {
924            Ok(rptr) => {
925                if rptr.as_uninit() != tptr {
926                    ReflectError::CustomSerializationError {
927                        message: "proxy convert_out did not return the expected pointer".into(),
928                        src_shape: self.shape,
929                        dst_shape: target_shape,
930                    }
931                } else {
932                    return Ok(Some(OwnedPeek {
933                        shape: target_shape,
934                        data: rptr,
935                        _phantom: PhantomData,
936                    }));
937                }
938            }
939            Err(message) => ReflectError::CustomSerializationError {
940                message,
941                src_shape: self.shape,
942                dst_shape: target_shape,
943            },
944        };
945
946        // if we reach here we have an error and we need to deallocate the target allocation
947        unsafe {
948            // SAFETY: unwrap should be ok since the allocation was ok
949            target_shape.deallocate_uninit(tptr).unwrap()
950        };
951        Err(err)
952    }
953}
954
955impl<'mem, 'facet> core::fmt::Display for Peek<'mem, 'facet> {
956    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
957        if let Some(result) = unsafe { self.shape.call_display(self.data, f) } {
958            return result;
959        }
960        write!(f, "⟨{}⟩", self.shape)
961    }
962}
963
964impl<'mem, 'facet> core::fmt::Debug for Peek<'mem, 'facet> {
965    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
966        if let Some(result) = unsafe { self.shape.call_debug(self.data, f) } {
967            return result;
968        }
969
970        write!(f, "⟨{}⟩", self.shape)
971    }
972}
973
974impl<'mem, 'facet> core::cmp::PartialEq for Peek<'mem, 'facet> {
975    #[inline]
976    fn eq(&self, other: &Self) -> bool {
977        self.partial_eq(other).unwrap_or(false)
978    }
979}
980
981impl<'mem, 'facet> core::cmp::PartialOrd for Peek<'mem, 'facet> {
982    #[inline]
983    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
984        self.partial_cmp(other).unwrap_or(None)
985    }
986}
987
988impl<'mem, 'facet> core::hash::Hash for Peek<'mem, 'facet> {
989    fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) {
990        self.hash(hasher)
991            .expect("Hashing is not supported for this shape");
992    }
993}
994
995/// A covariant wrapper around [`Peek`] for types that are covariant over their lifetime parameter.
996///
997/// Unlike [`Peek`], which is invariant over `'facet` for soundness reasons,
998/// `CovariantPeek` is **covariant** over `'facet`. This means a `CovariantPeek<'mem, 'static>`
999/// can be used where a `CovariantPeek<'mem, 'a>` is expected.
1000///
1001/// # When to Use
1002///
1003/// Use `CovariantPeek` when you need to:
1004/// - Store multiple `Peek` values with different lifetimes in a single collection
1005/// - Pass `Peek` values to functions expecting shorter lifetimes
1006/// - Build data structures that wrap `Peek` without forcing invariance on the wrapper
1007///
1008/// # Safety
1009///
1010/// `CovariantPeek` can only be constructed from types that are actually covariant.
1011/// The constructor verifies this at runtime by checking [`Shape::computed_variance`].
1012/// This ensures that lifetime shrinking is always safe.
1013///
1014/// # Example
1015///
1016/// ```
1017/// use facet::Facet;
1018/// use facet_reflect::{Peek, CovariantPeek};
1019///
1020/// #[derive(Facet)]
1021/// struct Data<'a> {
1022///     value: &'a str,
1023/// }
1024///
1025/// // Data<'a> is covariant in 'a because &'a str is covariant
1026/// let data = Data { value: "hello" };
1027/// let peek: Peek<'_, 'static> = Peek::new(&data);
1028///
1029/// // Convert to CovariantPeek - this verifies covariance
1030/// let covariant = CovariantPeek::new(peek).expect("Data is covariant");
1031///
1032/// // Now we can use it where shorter lifetimes are expected
1033/// fn use_shorter<'a>(p: CovariantPeek<'_, 'a>) {
1034///     let _ = p;
1035/// }
1036/// use_shorter(covariant);
1037/// ```
1038#[derive(Clone, Copy)]
1039pub struct CovariantPeek<'mem, 'facet> {
1040    /// Underlying data
1041    data: PtrConst,
1042
1043    /// Shape of the value
1044    shape: &'static Shape,
1045
1046    // Covariant over both 'mem and 'facet: CovariantPeek<'mem, 'static> can be used where
1047    // CovariantPeek<'mem, 'a> is expected.
1048    //
1049    // This is safe ONLY because we verify at construction time that the underlying
1050    // type is covariant. For covariant types, shrinking lifetimes is always safe.
1051    _covariant: PhantomData<(&'mem (), &'facet ())>,
1052}
1053
1054impl<'mem, 'facet> CovariantPeek<'mem, 'facet> {
1055    /// Creates a new `CovariantPeek` from a `Peek`, verifying that the underlying type is covariant.
1056    ///
1057    /// Returns `None` if the type is not covariant (i.e., it's contravariant or invariant).
1058    ///
1059    /// # Example
1060    ///
1061    /// ```
1062    /// use facet::Facet;
1063    /// use facet_reflect::{Peek, CovariantPeek};
1064    ///
1065    /// // i32 has no lifetime parameters, so it's covariant
1066    /// let value = 42i32;
1067    /// let peek = Peek::new(&value);
1068    /// let covariant = CovariantPeek::new(peek);
1069    /// assert!(covariant.is_some());
1070    /// ```
1071    #[inline]
1072    pub fn new(peek: Peek<'mem, 'facet>) -> Option<Self> {
1073        if peek.variance() == Variance::Covariant {
1074            Some(Self {
1075                data: peek.data,
1076                shape: peek.shape,
1077                _covariant: PhantomData,
1078            })
1079        } else {
1080            None
1081        }
1082    }
1083
1084    /// Creates a new `CovariantPeek` from a `Peek`, panicking if the type is not covariant.
1085    ///
1086    /// # Panics
1087    ///
1088    /// Panics if the underlying type is not covariant.
1089    ///
1090    /// # Example
1091    ///
1092    /// ```
1093    /// use facet::Facet;
1094    /// use facet_reflect::{Peek, CovariantPeek};
1095    ///
1096    /// let value = "hello";
1097    /// let peek = Peek::new(&value);
1098    /// let covariant = CovariantPeek::new_unchecked(peek); // Will succeed
1099    /// ```
1100    #[inline]
1101    pub fn new_unchecked(peek: Peek<'mem, 'facet>) -> Self {
1102        Self::new(peek).unwrap_or_else(|| {
1103            panic!(
1104                "CovariantPeek::new_unchecked called on non-covariant type {} (variance: {:?})",
1105                peek.shape,
1106                peek.variance()
1107            )
1108        })
1109    }
1110
1111    /// Creates a `CovariantPeek` directly from a covariant `Facet` type.
1112    ///
1113    /// Returns `None` if the type is not covariant.
1114    ///
1115    /// # Example
1116    ///
1117    /// ```
1118    /// use facet::Facet;
1119    /// use facet_reflect::CovariantPeek;
1120    ///
1121    /// let value = 42i32;
1122    /// let covariant = CovariantPeek::from_ref(&value);
1123    /// assert!(covariant.is_some());
1124    /// ```
1125    #[inline]
1126    pub fn from_ref<T: Facet<'facet> + ?Sized>(t: &'mem T) -> Option<Self> {
1127        Self::new(Peek::new(t))
1128    }
1129
1130    /// Returns the underlying `Peek`.
1131    ///
1132    /// Note that the returned `Peek` is invariant, so you cannot use it to
1133    /// shrink lifetimes directly. Use `CovariantPeek` for lifetime flexibility.
1134    #[inline]
1135    pub fn into_peek(self) -> Peek<'mem, 'facet> {
1136        Peek {
1137            data: self.data,
1138            shape: self.shape,
1139            _invariant: PhantomData,
1140        }
1141    }
1142
1143    /// Returns the shape of the underlying value.
1144    #[inline]
1145    pub const fn shape(&self) -> &'static Shape {
1146        self.shape
1147    }
1148
1149    /// Returns the data pointer.
1150    #[inline]
1151    pub const fn data(&self) -> PtrConst {
1152        self.data
1153    }
1154}
1155
1156impl<'mem, 'facet> core::ops::Deref for CovariantPeek<'mem, 'facet> {
1157    type Target = Peek<'mem, 'facet>;
1158
1159    #[inline]
1160    fn deref(&self) -> &Self::Target {
1161        // SAFETY: CovariantPeek and Peek have the same memory layout for the
1162        // data and shape fields. The PhantomData fields don't affect layout.
1163        // We're creating a reference to a Peek that views the same data.
1164        //
1165        // This is safe because:
1166        // 1. We only construct CovariantPeek from covariant types
1167        // 2. The Peek reference we return has the same lifetime bounds
1168        // 3. We're not allowing mutation through this reference
1169        unsafe { &*(self as *const CovariantPeek<'mem, 'facet> as *const Peek<'mem, 'facet>) }
1170    }
1171}
1172
1173impl<'mem, 'facet> core::fmt::Debug for CovariantPeek<'mem, 'facet> {
1174    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1175        f.debug_struct("CovariantPeek")
1176            .field("shape", &self.shape)
1177            .field("data", &self.data)
1178            .finish()
1179    }
1180}
1181
1182impl<'mem, 'facet> core::fmt::Display for CovariantPeek<'mem, 'facet> {
1183    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1184        core::fmt::Display::fmt(&**self, f)
1185    }
1186}
1187
1188#[cfg(test)]
1189mod tests {
1190    use super::*;
1191
1192    /// Regression test for issue #1082: UB in `Peek("").as_str()`
1193    /// Previously, `as_str()` used `get::<&str>()` which tried to read a fat pointer
1194    /// from the str data, causing UB for empty strings (reading 16 bytes from 0-byte allocation).
1195    #[test]
1196    fn test_peek_as_str_empty_string() {
1197        let peek = Peek::new("");
1198        assert_eq!(peek.as_str(), Some(""));
1199    }
1200
1201    #[test]
1202    fn test_peek_as_str_non_empty_string() {
1203        let peek = Peek::new("hello");
1204        assert_eq!(peek.as_str(), Some("hello"));
1205    }
1206
1207    #[test]
1208    #[cfg(feature = "alloc")]
1209    fn test_peek_as_str_owned_string() {
1210        let s = alloc::string::String::from("owned string");
1211        let peek = Peek::new(&s);
1212        assert_eq!(peek.as_str(), Some("owned string"));
1213    }
1214
1215    /// Regression test for issue #794: Peek::as_str() with double reference
1216    /// Previously, this would cause UB when trying to read &&str as &str
1217    #[test]
1218    fn test_peek_as_str_double_reference() {
1219        let value = &"hello";
1220        let peek = Peek::new(&value);
1221        assert_eq!(peek.as_str(), Some("hello"));
1222    }
1223
1224    #[test]
1225    fn test_covariant_peek_from_covariant_type() {
1226        // i32 has no lifetime parameters, so it's covariant
1227        let value = 42i32;
1228        let peek = Peek::new(&value);
1229        let covariant = CovariantPeek::new(peek);
1230        assert!(covariant.is_some());
1231
1232        // Verify we can access Peek methods through Deref
1233        let covariant = covariant.unwrap();
1234        assert_eq!(covariant.shape(), peek.shape());
1235    }
1236
1237    #[test]
1238    fn test_covariant_peek_from_ref() {
1239        let value = 42i32;
1240        let covariant = CovariantPeek::from_ref(&value);
1241        assert!(covariant.is_some());
1242    }
1243
1244    #[test]
1245    fn test_covariant_peek_deref_to_peek() {
1246        let value = "hello";
1247        let peek = Peek::new(&value);
1248        let covariant = CovariantPeek::new(peek).unwrap();
1249
1250        // Test that Deref works - we can call Peek methods directly
1251        assert_eq!(covariant.as_str(), Some("hello"));
1252        assert_eq!(covariant.shape(), peek.shape());
1253    }
1254
1255    #[test]
1256    fn test_covariant_peek_into_peek() {
1257        let value = 42i32;
1258        let original_peek = Peek::new(&value);
1259        let covariant = CovariantPeek::new(original_peek).unwrap();
1260        let recovered_peek = covariant.into_peek();
1261
1262        assert_eq!(recovered_peek.shape(), original_peek.shape());
1263    }
1264
1265    #[test]
1266    fn test_covariant_peek_lifetime_covariance() {
1267        // This test verifies that CovariantPeek is actually covariant over 'facet
1268        // by passing a CovariantPeek<'_, 'static> to a function expecting CovariantPeek<'_, 'a>
1269        fn use_shorter<'a>(_p: CovariantPeek<'_, 'a>) {}
1270
1271        let value = 42i32;
1272        let covariant: CovariantPeek<'_, 'static> = CovariantPeek::from_ref(&value).unwrap();
1273
1274        // This compiles because CovariantPeek is covariant over 'facet
1275        use_shorter(covariant);
1276    }
1277
1278    #[test]
1279    #[cfg(feature = "alloc")]
1280    fn test_covariant_peek_vec_type() {
1281        // Vec<T> is covariant in T
1282        let vec = alloc::vec![1i32, 2, 3];
1283        let peek = Peek::new(&vec);
1284        let covariant = CovariantPeek::new(peek);
1285        assert!(covariant.is_some());
1286    }
1287
1288    #[test]
1289    #[cfg(feature = "alloc")]
1290    fn test_covariant_peek_option_type() {
1291        // Option<T> is covariant in T
1292        let opt = Some(42i32);
1293        let peek = Peek::new(&opt);
1294        let covariant = CovariantPeek::new(peek);
1295        assert!(covariant.is_some());
1296    }
1297
1298    #[test]
1299    fn test_spanned_structural_hash_ignores_span() {
1300        use crate::{Span, Spanned};
1301        use core::hash::Hasher;
1302        use std::hash::DefaultHasher;
1303
1304        // Two Spanned values with same inner value but different spans
1305        let a = Spanned::new(42i32, Span::new(0, 10));
1306        let b = Spanned::new(42i32, Span::new(100, 20));
1307
1308        // They should have the same structural hash
1309        let mut hasher_a = DefaultHasher::new();
1310        Peek::new(&a).structural_hash(&mut hasher_a);
1311        let hash_a = hasher_a.finish();
1312
1313        let mut hasher_b = DefaultHasher::new();
1314        Peek::new(&b).structural_hash(&mut hasher_b);
1315        let hash_b = hasher_b.finish();
1316
1317        assert_eq!(
1318            hash_a, hash_b,
1319            "Spanned values with same inner value should have same structural hash"
1320        );
1321    }
1322
1323    #[test]
1324    fn test_spanned_structural_hash_differs_for_different_values() {
1325        use crate::{Span, Spanned};
1326        use core::hash::Hasher;
1327        use std::hash::DefaultHasher;
1328
1329        // Two Spanned values with different inner values
1330        let a = Spanned::new(42i32, Span::new(0, 10));
1331        let b = Spanned::new(99i32, Span::new(0, 10));
1332
1333        // They should have different structural hashes
1334        let mut hasher_a = DefaultHasher::new();
1335        Peek::new(&a).structural_hash(&mut hasher_a);
1336        let hash_a = hasher_a.finish();
1337
1338        let mut hasher_b = DefaultHasher::new();
1339        Peek::new(&b).structural_hash(&mut hasher_b);
1340        let hash_b = hasher_b.finish();
1341
1342        assert_ne!(
1343            hash_a, hash_b,
1344            "Spanned values with different inner values should have different structural hashes"
1345        );
1346    }
1347
1348    #[test]
1349    fn test_spanned_field_metadata() {
1350        use crate::Spanned;
1351        use facet_core::{Type, UserType};
1352
1353        // Get the shape for Spanned<i32>
1354        let shape = <Spanned<i32> as facet_core::Facet>::SHAPE;
1355
1356        // Extract the struct type
1357        let struct_type = match shape.ty {
1358            Type::User(UserType::Struct(st)) => st,
1359            _ => panic!("Expected struct type"),
1360        };
1361
1362        // Find the span field and verify it has metadata = "span"
1363        let span_field = struct_type
1364            .fields
1365            .iter()
1366            .find(|f| f.name == "span")
1367            .expect("Should have span field");
1368
1369        assert!(
1370            span_field.is_metadata(),
1371            "span field should be marked as metadata"
1372        );
1373        assert_eq!(
1374            span_field.metadata_kind(),
1375            Some("span"),
1376            "span field should have metadata kind 'span'"
1377        );
1378
1379        // Verify the value field is NOT metadata
1380        let value_field = struct_type
1381            .fields
1382            .iter()
1383            .find(|f| f.name == "value")
1384            .expect("Should have value field");
1385
1386        assert!(
1387            !value_field.is_metadata(),
1388            "value field should not be marked as metadata"
1389        );
1390    }
1391}