facet_reflect/peek/
value.rs

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