facet_types/
value.rs

1use bitflags::bitflags;
2use facet_opaque::{Opaque, OpaqueConst, OpaqueUninit};
3use std::cmp::Ordering;
4
5use crate::Shape;
6
7//======== Type Information ========
8
9/// A function that formats the name of a type.
10///
11/// This helps avoid allocations, and it takes options.
12pub type TypeNameFn = fn(f: &mut std::fmt::Formatter, opts: TypeNameOpts) -> std::fmt::Result;
13
14/// Options for formatting the name of a type
15#[non_exhaustive]
16#[derive(Clone, Copy)]
17pub struct TypeNameOpts {
18    /// as long as this is > 0, keep formatting the type parameters
19    /// when it reaches 0, format type parameters as `...`
20    /// if negative, all type parameters are formatted
21    pub recurse_ttl: isize,
22}
23
24impl Default for TypeNameOpts {
25    fn default() -> Self {
26        Self { recurse_ttl: -1 }
27    }
28}
29
30impl TypeNameOpts {
31    /// Create a new `NameOpts` for which none of the type parameters are formatted
32    pub fn none() -> Self {
33        Self { recurse_ttl: 0 }
34    }
35
36    /// Create a new `NameOpts` for which only the direct children are formatted
37    pub fn one() -> Self {
38        Self { recurse_ttl: 1 }
39    }
40
41    /// Create a new `NameOpts` for which all type parameters are formatted
42    pub fn infinite() -> Self {
43        Self { recurse_ttl: -1 }
44    }
45
46    /// Decrease the `recurse_ttl` — if it's != 0, returns options to pass when
47    /// formatting children type parameters.
48    ///
49    /// If this returns `None` and you have type parameters, you should render a
50    /// `…` (unicode ellipsis) character instead of your list of types.
51    ///
52    /// See the implementation for `Vec` for examples.
53    pub fn for_children(&self) -> Option<Self> {
54        match self.recurse_ttl.cmp(&0) {
55            Ordering::Greater => Some(Self {
56                recurse_ttl: self.recurse_ttl - 1,
57            }),
58            Ordering::Less => Some(Self {
59                recurse_ttl: self.recurse_ttl,
60            }),
61            Ordering::Equal => None,
62        }
63    }
64}
65
66//======== Memory Management ========
67
68/// Function to drop a value
69///
70/// # Safety
71///
72/// The `value` parameter must point to aligned, initialized memory of the correct type.
73pub type DropInPlaceFn = for<'mem> unsafe fn(value: Opaque<'mem>);
74
75/// Generates a [`DropInPlaceFn`] for a concrete type
76pub const fn drop_in_place_fn_for<T>() -> Option<DropInPlaceFn> {
77    Some(|value: Opaque<'_>| unsafe {
78        value.drop_in_place::<T>();
79    })
80}
81
82/// Function to clone a value into another already-allocated value
83///
84/// # Safety
85///
86/// The `source` parameter must point to aligned, initialized memory of the correct type.
87/// The `target` parameter has the correct layout and alignment, but points to
88/// uninitialized memory. The function returns the same pointer wrapped in an [`Opaque`].
89pub type CloneIntoFn = for<'src, 'dst> unsafe fn(
90    source: OpaqueConst<'src>,
91    target: OpaqueUninit<'dst>,
92) -> Opaque<'dst>;
93
94/// Generates a [`CloneInPlaceFn`] for a concrete type
95pub const fn clone_into_fn_for<T: Clone>() -> Option<CloneIntoFn> {
96    Some(|source: OpaqueConst<'_>, target: OpaqueUninit<'_>| unsafe {
97        let source_val = source.as_ref::<T>();
98        target.write(source_val.clone())
99    })
100}
101
102/// Function to set a value to its default in-place
103///
104/// # Safety
105///
106/// The `target` parameter has the correct layout and alignment, but points to
107/// uninitialized memory. The function returns the same pointer wrapped in an [`Opaque`].
108pub type DefaultInPlaceFn = for<'mem> unsafe fn(target: OpaqueUninit<'mem>) -> Opaque<'mem>;
109
110/// Generates a [`DefaultInPlaceFn`] for a concrete type
111pub const fn default_in_place_fn_for<T: Default>() -> Option<DefaultInPlaceFn> {
112    Some(|target: OpaqueUninit<'_>| unsafe { target.write(T::default()) })
113}
114
115//======== Conversion ========
116
117/// Function to parse a value from a string.
118///
119/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
120///
121/// # Safety
122///
123/// The `target` parameter has the correct layout and alignment, but points to
124/// uninitialized memory. If this function succeeds, it should return `Ok` with the
125/// same pointer wrapped in an [`Opaque`]. If parsing fails, it returns `Err` with an error.
126pub type ParseFn =
127    for<'mem> unsafe fn(s: &str, target: OpaqueUninit<'mem>) -> Result<Opaque<'mem>, ParseError>;
128
129/// Generates a [`ParseFn`] for a concrete type
130pub const fn parse_fn_for<T: std::str::FromStr>() -> Option<ParseFn> {
131    Some(|s: &str, target: OpaqueUninit<'_>| unsafe {
132        match s.parse::<T>() {
133            Ok(value) => Ok(target.write(value)),
134            Err(_) => Err(ParseError::Generic("failed to parse string")),
135        }
136    })
137}
138
139/// Error returned by [`ParseFn`]
140#[non_exhaustive]
141#[derive(Debug)]
142pub enum ParseError {
143    /// Generic error message
144    Generic(&'static str),
145}
146
147impl std::fmt::Display for ParseError {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        match self {
150            ParseError::Generic(msg) => write!(f, "Parse failed: {}", msg),
151        }
152    }
153}
154
155impl std::error::Error for ParseError {}
156
157/// Function to try converting from another type
158///
159/// # Safety
160///
161/// The `target` parameter has the correct layout and alignment, but points to
162/// uninitialized memory. If this function succeeds, it should return `Ok` with the
163/// same pointer wrapped in an [`Opaque`]. If conversion fails, it returns `Err` with an error.
164pub type TryFromFn = for<'src, 'mem> unsafe fn(
165    source: OpaqueConst<'src>,
166    target: OpaqueUninit<'mem>,
167) -> Result<Opaque<'mem>, TryFromError>;
168
169/// Error type for TryFrom conversion failures
170#[non_exhaustive]
171#[derive(Debug)]
172pub enum TryFromError {
173    /// Generic conversion error
174    Generic(&'static str),
175    /// The target shape doesn't implement conversion from any source shape (no try_from in vtable)
176    Unimplemented(&'static Shape),
177    /// The target shape has a conversion implementation, but it doesn't support converting from this specific source shape
178    Incompatible {
179        /// The source shape that we tried to convert from
180        source: &'static Shape,
181        /// The target shape that we tried to convert to
182        target: &'static Shape,
183    },
184}
185
186impl std::fmt::Display for TryFromError {
187    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188        match self {
189            TryFromError::Generic(msg) => write!(f, "Conversion failed: {}", msg),
190            TryFromError::Unimplemented(shape) => write!(
191                f,
192                "Conversion failed: Shape {} doesn't implement any conversions (no try_from function)",
193                shape
194            ),
195            TryFromError::Incompatible { source, target } => write!(
196                f,
197                "Conversion failed: Cannot convert from shape {} to shape {}",
198                source, target
199            ),
200        }
201    }
202}
203
204impl std::error::Error for TryFromError {}
205
206//======== Comparison ========
207
208/// Function to check if two values are partially equal
209///
210/// # Safety
211///
212/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
213pub type PartialEqFn = for<'l, 'r> unsafe fn(left: OpaqueConst<'l>, right: OpaqueConst<'r>) -> bool;
214
215/// Generates a [`PartialEqFn`] for a concrete type
216pub const fn partial_eq_fn_for<T: PartialEq>() -> Option<PartialEqFn> {
217    Some(|left: OpaqueConst<'_>, right: OpaqueConst<'_>| -> bool {
218        let left_val = unsafe { left.as_ref::<T>() };
219        let right_val = unsafe { right.as_ref::<T>() };
220        left_val == right_val
221    })
222}
223
224/// Function to compare two values and return their ordering if comparable
225///
226/// # Safety
227///
228/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
229pub type PartialOrdFn =
230    for<'l, 'r> unsafe fn(left: OpaqueConst<'l>, right: OpaqueConst<'r>) -> Option<Ordering>;
231
232/// Generates a [`PartialOrdFn`] for a concrete type
233pub const fn partial_ord_fn_for<T: PartialOrd>() -> Option<PartialOrdFn> {
234    Some(
235        |left: OpaqueConst<'_>, right: OpaqueConst<'_>| -> Option<Ordering> {
236            let left_val = unsafe { left.as_ref::<T>() };
237            let right_val = unsafe { right.as_ref::<T>() };
238            left_val.partial_cmp(right_val)
239        },
240    )
241}
242
243/// Function to compare two values and return their ordering
244///
245/// # Safety
246///
247/// Both `left` and `right` parameters must point to aligned, initialized memory of the correct type.
248pub type CmpFn = for<'l, 'r> unsafe fn(left: OpaqueConst<'l>, right: OpaqueConst<'r>) -> Ordering;
249
250/// Generates a [`CmpFn`] for a concrete type
251pub const fn cmp_fn_for<T: Ord>() -> Option<CmpFn> {
252    Some(
253        |left: OpaqueConst<'_>, right: OpaqueConst<'_>| -> Ordering {
254            let left_val = unsafe { left.as_ref::<T>() };
255            let right_val = unsafe { right.as_ref::<T>() };
256            left_val.cmp(right_val)
257        },
258    )
259}
260
261//======== Hashing ========
262
263/// Function to hash a value
264///
265/// # Safety
266///
267/// The `value` parameter must point to aligned, initialized memory of the correct type.
268/// The hasher pointer must be a valid pointer to a Hasher trait object.
269pub type HashFn = for<'mem> unsafe fn(
270    value: OpaqueConst<'mem>,
271    hasher_this: Opaque<'mem>,
272    hasher_write_fn: HasherWriteFn,
273);
274
275/// Generates a [`HashFn`] for a concrete type
276pub const fn hash_fn_for<T: std::hash::Hash>() -> Option<HashFn> {
277    Some(
278        |value: OpaqueConst<'_>, hasher_this: Opaque<'_>, hasher_write_fn: HasherWriteFn| unsafe {
279            let val = value.as_ref::<T>();
280            val.hash(&mut HasherProxy::new(hasher_this, hasher_write_fn));
281        },
282    )
283}
284
285/// Function to write bytes to a hasher
286///
287/// # Safety
288///
289/// The `hasher_self` parameter must be a valid pointer to a hasher
290pub type HasherWriteFn = for<'mem> unsafe fn(hasher_self: Opaque<'mem>, bytes: &[u8]);
291
292/// Provides an implementation of [`std::hash::Hasher`] for a given hasher pointer and write function
293///
294/// See [`HashFn`] for more details on the parameters.
295///
296/// Example usage (for a type that already implements `Hasher`)
297///
298/// ```rust,ignore
299/// hash: Some(|value, hasher_self, hasher_write_fn| unsafe {
300///     value
301///         .as_ref::<Self>()
302///         .hash(&mut HasherProxy::new(hasher_self, hasher_write_fn));
303/// }),
304/// ```
305pub struct HasherProxy<'a> {
306    hasher_this: Opaque<'a>,
307    hasher_write_fn: HasherWriteFn,
308}
309
310impl<'a> HasherProxy<'a> {
311    /// Create a new `HasherProxy` from a hasher pointer and a write function
312    ///
313    /// # Safety
314    ///
315    /// The `hasher_this` parameter must be a valid pointer to a Hasher trait object.
316    /// The `hasher_write_fn` parameter must be a valid function pointer.
317    pub unsafe fn new(hasher_this: Opaque<'a>, hasher_write_fn: HasherWriteFn) -> Self {
318        Self {
319            hasher_this,
320            hasher_write_fn,
321        }
322    }
323}
324
325impl std::hash::Hasher for HasherProxy<'_> {
326    fn finish(&self) -> u64 {
327        unimplemented!("finish is not needed for this implementation")
328    }
329    fn write(&mut self, bytes: &[u8]) {
330        unsafe { (self.hasher_write_fn)(self.hasher_this, bytes) }
331    }
332}
333
334//======== Marker Traits ========
335
336bitflags! {
337    /// Bitflags for common marker traits that a type may implement
338    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
339    pub struct MarkerTraits: u8 {
340        /// Indicates that the type implements the [`Eq`] marker trait
341        const EQ = 1 << 0;
342        /// Indicates that the type implements the [`Send`] marker trait
343        const SEND = 1 << 1;
344        /// Indicates that the type implements the [`Sync`] marker trait
345        const SYNC = 1 << 2;
346        /// Indicates that the type implements the [`Copy`] marker trait
347        const COPY = 1 << 3;
348    }
349}
350
351//======== Display and Debug ========
352
353/// Function to format a value for display
354///
355/// If both [`DisplayFn`] and [`ParseFn`] are set, we should be able to round-trip the value.
356///
357/// # Safety
358///
359/// The `value` parameter must point to aligned, initialized memory of the correct type.
360pub type DisplayFn =
361    for<'mem> unsafe fn(value: OpaqueConst<'mem>, f: &mut std::fmt::Formatter) -> std::fmt::Result;
362
363/// Generates a [`DisplayFn`] for a concrete type
364pub const fn display_fn_for<T: std::fmt::Display>() -> Option<DisplayFn> {
365    Some(
366        |value: OpaqueConst<'_>, f: &mut std::fmt::Formatter| -> std::fmt::Result {
367            let val = unsafe { value.as_ref::<T>() };
368            write!(f, "{val}")
369        },
370    )
371}
372
373/// Function to format a value for debug.
374/// If this returns None, the shape did not implement Debug.
375///
376/// # Safety
377///
378/// The `value` parameter must point to aligned, initialized memory of the correct type.
379pub type DebugFn =
380    for<'mem> unsafe fn(value: OpaqueConst<'mem>, f: &mut std::fmt::Formatter) -> std::fmt::Result;
381
382/// Generates a [`DebugFn`] for a concrete type
383pub const fn debug_fn_for<T: std::fmt::Debug>() -> Option<DebugFn> {
384    Some(
385        |value: OpaqueConst<'_>, f: &mut std::fmt::Formatter| -> std::fmt::Result {
386            let val = unsafe { value.as_ref::<T>() };
387            write!(f, "{val:?}")
388        },
389    )
390}
391
392/// VTable for common operations that can be performed on any shape
393#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
394pub struct ValueVTable {
395    /// cf. [`TypeNameFn`]
396    pub type_name: TypeNameFn,
397
398    /// cf. [`DisplayFn`]
399    pub display: Option<DisplayFn>,
400
401    /// cf. [`DebugFn`]
402    pub debug: Option<DebugFn>,
403
404    /// cf. [`DefaultInPlaceFn`]
405    pub default_in_place: Option<DefaultInPlaceFn>,
406
407    /// cf. [`CloneInPlaceFn`]
408    pub clone_into: Option<CloneIntoFn>,
409
410    /// Marker traits implemented by the type
411    // FIXME: move out of vtable, it's not really... functions.
412    // Belongs in Shape directly.
413    pub marker_traits: MarkerTraits,
414
415    /// cf. [`PartialEqFn`] for equality comparison
416    pub eq: Option<PartialEqFn>,
417
418    /// cf. [`PartialOrdFn`] for partial ordering comparison
419    pub partial_ord: Option<PartialOrdFn>,
420
421    /// cf. [`CmpFn`] for total ordering
422    pub ord: Option<CmpFn>,
423
424    /// cf. [`HashFn`]
425    pub hash: Option<HashFn>,
426
427    /// cf. [`DropInPlaceFn`] — if None, drops without side-effects
428    pub drop_in_place: Option<DropInPlaceFn>,
429
430    /// cf. [`ParseFn`]
431    pub parse: Option<ParseFn>,
432
433    /// cf. [`TryFromFn`]
434    pub try_from: Option<TryFromFn>,
435}
436
437impl ValueVTable {
438    /// Check if the type implements the [`Eq`] marker trait
439    pub fn is_eq(&self) -> bool {
440        self.marker_traits.contains(MarkerTraits::EQ)
441    }
442
443    /// Check if the type implements the [`Send`] marker trait
444    pub fn is_send(&self) -> bool {
445        self.marker_traits.contains(MarkerTraits::SEND)
446    }
447
448    /// Check if the type implements the [`Sync`] marker trait
449    pub fn is_sync(&self) -> bool {
450        self.marker_traits.contains(MarkerTraits::SYNC)
451    }
452
453    /// Check if the type implements the [`Copy`] marker trait
454    pub fn is_copy(&self) -> bool {
455        self.marker_traits.contains(MarkerTraits::COPY)
456    }
457}