facet_reflect/peek/
value.rs

1use core::cmp::Ordering;
2use facet_core::{Opaque, OpaqueConst, Shape, TypeNameOpts, ValueVTable};
3
4use crate::Peek;
5
6/// Lets you read from a value (implements read-only [`ValueVTable`] proxies)
7#[derive(Clone, Copy)]
8pub struct PeekValue<'mem> {
9    data: OpaqueConst<'mem>,
10    shape: &'static Shape,
11}
12
13impl core::fmt::Display for PeekValue<'_> {
14    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
15        if let Some(display_fn) = self.vtable().display {
16            unsafe { display_fn(self.data, f) }
17        } else {
18            write!(f, "⟨{}⟩", self.shape)
19        }
20    }
21}
22
23impl core::fmt::Debug for PeekValue<'_> {
24    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25        if let Some(debug_fn) = self.vtable().debug {
26            unsafe { debug_fn(self.data, f) }
27        } else {
28            write!(f, "⟨{}⟩", self.shape)
29        }
30    }
31}
32
33impl core::cmp::PartialEq for PeekValue<'_> {
34    fn eq(&self, other: &Self) -> bool {
35        if self.shape != other.shape {
36            return false;
37        }
38        let eq_fn = match self.shape.vtable.eq {
39            Some(eq_fn) => eq_fn,
40            None => return false,
41        };
42        unsafe { eq_fn(self.data, other.data) }
43    }
44}
45
46impl core::cmp::PartialOrd for PeekValue<'_> {
47    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
48        if self.shape != other.shape {
49            return None;
50        }
51        let partial_ord_fn = self.shape.vtable.partial_ord?;
52        unsafe { partial_ord_fn(self.data, other.data) }
53    }
54}
55
56impl<'mem> PeekValue<'mem> {
57    /// Creates a new `PeekValue` instance.
58    ///
59    /// # Safety
60    ///
61    /// `data` must be initialized and well-aligned, and point to a value
62    /// of the type described by `shape`.
63    pub(crate) unsafe fn unchecked_new(data: OpaqueConst<'mem>, shape: &'static Shape) -> Self {
64        Self { data, shape }
65    }
66
67    /// Returns the vtable
68    #[inline(always)]
69    pub fn vtable(&self) -> &'static ValueVTable {
70        self.shape.vtable
71    }
72
73    /// Returns true if this scalar is equal to the other scalar
74    ///
75    /// # Returns
76    ///
77    /// `false` if equality comparison is not supported for this scalar type
78    #[inline]
79    pub fn eq(&self, other: &PeekValue<'_>) -> Option<bool> {
80        unsafe {
81            self.shape
82                .vtable
83                .eq
84                .map(|eq_fn| eq_fn(self.data, other.data))
85        }
86    }
87
88    /// Compares this scalar with another and returns their ordering
89    ///
90    /// # Returns
91    ///
92    /// `None` if comparison is not supported for this scalar type
93    #[inline]
94    pub fn partial_cmp(&self, other: &PeekValue<'_>) -> Option<Ordering> {
95        unsafe {
96            self.shape
97                .vtable
98                .partial_ord
99                .and_then(|partial_ord_fn| partial_ord_fn(self.data, other.data))
100        }
101    }
102
103    /// Compares this scalar with another and returns their ordering
104    ///
105    /// # Returns
106    ///
107    /// `None` if comparison is not supported for this scalar type
108    #[inline]
109    // Note: we cannot implement `Ord` for `PeekValue` because the underlying shape might
110    // not implement `Ord` — unlike the `PartialOrd` case where we can just pretend it
111    // could not order two specific values.
112    #[expect(clippy::should_implement_trait)]
113    pub fn cmp(&self, other: &PeekValue<'_>) -> Option<Ordering> {
114        unsafe {
115            self.shape
116                .vtable
117                .ord
118                .map(|ord_fn| ord_fn(self.data, other.data))
119        }
120    }
121
122    /// Returns true if this scalar is greater than the other scalar
123    ///
124    /// # Returns
125    ///
126    /// `false` if comparison is not supported for this scalar type
127    #[inline]
128    pub fn gt(&self, other: &PeekValue<'_>) -> bool {
129        self.cmp(other)
130            .map(|ordering| ordering == Ordering::Greater)
131            .unwrap_or(false)
132    }
133
134    /// Returns true if this scalar is greater than or equal to the other scalar
135    ///
136    /// # Returns
137    ///
138    /// `false` if comparison is not supported for this scalar type
139    #[inline]
140    pub fn gte(&self, other: &PeekValue<'_>) -> bool {
141        self.cmp(other)
142            .map(|ordering| ordering == Ordering::Greater || ordering == Ordering::Equal)
143            .unwrap_or(false)
144    }
145
146    /// Returns true if this scalar is less than the other scalar
147    ///
148    /// # Returns
149    ///
150    /// `false` if comparison is not supported for this scalar type
151    #[inline]
152    pub fn lt(&self, other: &PeekValue<'_>) -> bool {
153        self.cmp(other)
154            .map(|ordering| ordering == Ordering::Less)
155            .unwrap_or(false)
156    }
157
158    /// Returns true if this scalar is less than or equal to the other scalar
159    ///
160    /// # Returns
161    ///
162    /// `false` if comparison is not supported for this scalar type
163    #[inline(always)]
164    pub fn lte(&self, other: &PeekValue<'_>) -> bool {
165        self.cmp(other)
166            .map(|ordering| ordering == Ordering::Less || ordering == Ordering::Equal)
167            .unwrap_or(false)
168    }
169
170    /// Formats this scalar for display
171    ///
172    /// # Returns
173    ///
174    /// `None` if display formatting is not supported for this scalar type
175    #[inline(always)]
176    pub fn display(&self, f: &mut core::fmt::Formatter<'_>) -> Option<core::fmt::Result> {
177        unsafe {
178            self.shape
179                .vtable
180                .display
181                .map(|display_fn| display_fn(self.data, f))
182        }
183    }
184
185    /// Formats this scalar for debug
186    ///
187    /// # Returns
188    ///
189    /// `None` if debug formatting is not supported for this scalar type
190    #[inline(always)]
191    pub fn debug(&self, f: &mut core::fmt::Formatter<'_>) -> Option<core::fmt::Result> {
192        unsafe {
193            self.shape
194                .vtable
195                .debug
196                .map(|debug_fn| debug_fn(self.data, f))
197        }
198    }
199
200    /// Hashes this scalar
201    ///
202    /// # Returns
203    ///
204    /// `false` if hashing is not supported for this scalar type, `true` otherwise
205    #[inline(always)]
206    pub fn hash<H: core::hash::Hasher>(&self, hasher: &mut H) -> bool {
207        unsafe {
208            if let Some(hash_fn) = self.shape.vtable.hash {
209                let hasher_opaque = Opaque::new(hasher);
210                hash_fn(self.data, hasher_opaque, |opaque, bytes| {
211                    opaque.as_mut::<H>().write(bytes)
212                });
213                true
214            } else {
215                false
216            }
217        }
218    }
219
220    /// Returns the type name of this scalar
221    ///
222    /// # Arguments
223    ///
224    /// * `f` - A mutable reference to a `core::fmt::Formatter`
225    /// * `opts` - The `TypeNameOpts` to use for formatting
226    ///
227    /// # Returns
228    ///
229    /// The result of the type name formatting
230    #[inline(always)]
231    pub fn type_name(
232        &self,
233        f: &mut core::fmt::Formatter<'_>,
234        opts: TypeNameOpts,
235    ) -> core::fmt::Result {
236        (self.shape.vtable.type_name)(f, opts)
237    }
238
239    /// Returns the shape
240    #[inline(always)]
241    pub const fn shape(&self) -> &'static Shape {
242        self.shape
243    }
244
245    /// Returns the data
246    #[inline(always)]
247    pub const fn data(&self) -> OpaqueConst<'mem> {
248        self.data
249    }
250
251    /// Wraps this scalar back into a `Peek`
252    #[inline(always)]
253    pub fn wrap(self) -> Peek<'mem> {
254        unsafe { Peek::unchecked_new(self.data, self.shape) }
255    }
256}