facet_types/
value.rs

1use bitflags::bitflags;
2use core::cmp::Ordering;
3use facet_opaque::{Opaque, OpaqueConst, OpaqueUninit};
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 core::fmt::Formatter, opts: TypeNameOpts) -> core::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: core::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 core::fmt::Display for ParseError {
148    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
149        match self {
150            ParseError::Generic(msg) => write!(f, "Parse failed: {}", msg),
151        }
152    }
153}
154
155impl core::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 core::fmt::Display for TryFromError {
187    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::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 core::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: core::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 [`core::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 core::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 = for<'mem> unsafe fn(
361    value: OpaqueConst<'mem>,
362    f: &mut core::fmt::Formatter,
363) -> core::fmt::Result;
364
365/// Generates a [`DisplayFn`] for a concrete type
366pub const fn display_fn_for<T: core::fmt::Display>() -> Option<DisplayFn> {
367    Some(
368        |value: OpaqueConst<'_>, f: &mut core::fmt::Formatter| -> core::fmt::Result {
369            let val = unsafe { value.as_ref::<T>() };
370            write!(f, "{val}")
371        },
372    )
373}
374
375/// Function to format a value for debug.
376/// If this returns None, the shape did not implement Debug.
377///
378/// # Safety
379///
380/// The `value` parameter must point to aligned, initialized memory of the correct type.
381pub type DebugFn = for<'mem> unsafe fn(
382    value: OpaqueConst<'mem>,
383    f: &mut core::fmt::Formatter,
384) -> core::fmt::Result;
385
386/// Generates a [`DebugFn`] for a concrete type
387pub const fn debug_fn_for<T: core::fmt::Debug>() -> Option<DebugFn> {
388    Some(
389        |value: OpaqueConst<'_>, f: &mut core::fmt::Formatter| -> core::fmt::Result {
390            let val = unsafe { value.as_ref::<T>() };
391            write!(f, "{val:?}")
392        },
393    )
394}
395
396/// VTable for common operations that can be performed on any shape
397#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
398#[non_exhaustive]
399pub struct ValueVTable {
400    /// cf. [`TypeNameFn`]
401    pub type_name: TypeNameFn,
402
403    /// cf. [`DisplayFn`]
404    pub display: Option<DisplayFn>,
405
406    /// cf. [`DebugFn`]
407    pub debug: Option<DebugFn>,
408
409    /// cf. [`DefaultInPlaceFn`]
410    pub default_in_place: Option<DefaultInPlaceFn>,
411
412    /// cf. [`CloneInPlaceFn`]
413    pub clone_into: Option<CloneIntoFn>,
414
415    /// Marker traits implemented by the type
416    // FIXME: move out of vtable, it's not really... functions.
417    // Belongs in Shape directly.
418    pub marker_traits: MarkerTraits,
419
420    /// cf. [`PartialEqFn`] for equality comparison
421    pub eq: Option<PartialEqFn>,
422
423    /// cf. [`PartialOrdFn`] for partial ordering comparison
424    pub partial_ord: Option<PartialOrdFn>,
425
426    /// cf. [`CmpFn`] for total ordering
427    pub ord: Option<CmpFn>,
428
429    /// cf. [`HashFn`]
430    pub hash: Option<HashFn>,
431
432    /// cf. [`DropInPlaceFn`] — if None, drops without side-effects
433    pub drop_in_place: Option<DropInPlaceFn>,
434
435    /// cf. [`ParseFn`]
436    pub parse: Option<ParseFn>,
437
438    /// cf. [`TryFromFn`]
439    pub try_from: Option<TryFromFn>,
440}
441
442impl ValueVTable {
443    /// Check if the type implements the [`Eq`] marker trait
444    pub fn is_eq(&self) -> bool {
445        self.marker_traits.contains(MarkerTraits::EQ)
446    }
447
448    /// Check if the type implements the [`Send`] marker trait
449    pub fn is_send(&self) -> bool {
450        self.marker_traits.contains(MarkerTraits::SEND)
451    }
452
453    /// Check if the type implements the [`Sync`] marker trait
454    pub fn is_sync(&self) -> bool {
455        self.marker_traits.contains(MarkerTraits::SYNC)
456    }
457
458    /// Check if the type implements the [`Copy`] marker trait
459    pub fn is_copy(&self) -> bool {
460        self.marker_traits.contains(MarkerTraits::COPY)
461    }
462
463    /// Creates a new [`ValueVTableBuilder`]
464    pub const fn builder() -> ValueVTableBuilder {
465        ValueVTableBuilder::new()
466    }
467}
468
469/// Builds a [`ValueVTable`]
470pub struct ValueVTableBuilder {
471    type_name: Option<TypeNameFn>,
472    display: Option<DisplayFn>,
473    debug: Option<DebugFn>,
474    default_in_place: Option<DefaultInPlaceFn>,
475    clone_into: Option<CloneIntoFn>,
476    marker_traits: MarkerTraits,
477    eq: Option<PartialEqFn>,
478    partial_ord: Option<PartialOrdFn>,
479    ord: Option<CmpFn>,
480    hash: Option<HashFn>,
481    drop_in_place: Option<DropInPlaceFn>,
482    parse: Option<ParseFn>,
483    try_from: Option<TryFromFn>,
484}
485
486impl ValueVTableBuilder {
487    /// Creates a new [`ValueVTableBuilder`] with all fields set to `None`.
488    #[allow(clippy::new_without_default)]
489    pub const fn new() -> Self {
490        Self {
491            type_name: None,
492            display: None,
493            debug: None,
494            default_in_place: None,
495            clone_into: None,
496            marker_traits: MarkerTraits::empty(),
497            eq: None,
498            partial_ord: None,
499            ord: None,
500            hash: None,
501            drop_in_place: None,
502            parse: None,
503            try_from: None,
504        }
505    }
506
507    /// Sets the type name function for this builder.
508    pub const fn type_name(mut self, type_name: TypeNameFn) -> Self {
509        self.type_name = Some(type_name);
510        self
511    }
512
513    /// Sets the display function for this builder.
514    pub const fn display(mut self, display: DisplayFn) -> Self {
515        self.display = Some(display);
516        self
517    }
518
519    /// Sets the display function for this builder if Some.
520    pub const fn display_maybe(mut self, display: Option<DisplayFn>) -> Self {
521        self.display = display;
522        self
523    }
524
525    /// Sets the debug function for this builder.
526    pub const fn debug(mut self, debug: DebugFn) -> Self {
527        self.debug = Some(debug);
528        self
529    }
530
531    /// Sets the debug function for this builder if Some.
532    pub const fn debug_maybe(mut self, debug: Option<DebugFn>) -> Self {
533        self.debug = debug;
534        self
535    }
536
537    /// Sets the default_in_place function for this builder.
538    pub const fn default_in_place(mut self, default_in_place: DefaultInPlaceFn) -> Self {
539        self.default_in_place = Some(default_in_place);
540        self
541    }
542
543    /// Sets the default_in_place function for this builder if Some.
544    pub const fn default_in_place_maybe(
545        mut self,
546        default_in_place: Option<DefaultInPlaceFn>,
547    ) -> Self {
548        self.default_in_place = default_in_place;
549        self
550    }
551
552    /// Sets the clone_into function for this builder.
553    pub const fn clone_into(mut self, clone_into: CloneIntoFn) -> Self {
554        self.clone_into = Some(clone_into);
555        self
556    }
557
558    /// Sets the clone_into function for this builder if Some.
559    pub const fn clone_into_maybe(mut self, clone_into: Option<CloneIntoFn>) -> Self {
560        self.clone_into = clone_into;
561        self
562    }
563
564    /// Sets the marker traits for this builder.
565    pub const fn marker_traits(mut self, marker_traits: MarkerTraits) -> Self {
566        self.marker_traits = marker_traits;
567        self
568    }
569
570    /// Sets the eq function for this builder.
571    pub const fn eq(mut self, eq: PartialEqFn) -> Self {
572        self.eq = Some(eq);
573        self
574    }
575
576    /// Sets the eq function for this builder if Some.
577    pub const fn eq_maybe(mut self, eq: Option<PartialEqFn>) -> Self {
578        self.eq = eq;
579        self
580    }
581
582    /// Sets the partial_ord function for this builder.
583    pub const fn partial_ord(mut self, partial_ord: PartialOrdFn) -> Self {
584        self.partial_ord = Some(partial_ord);
585        self
586    }
587
588    /// Sets the partial_ord function for this builder if Some.
589    pub const fn partial_ord_maybe(mut self, partial_ord: Option<PartialOrdFn>) -> Self {
590        self.partial_ord = partial_ord;
591        self
592    }
593
594    /// Sets the ord function for this builder.
595    pub const fn ord(mut self, ord: CmpFn) -> Self {
596        self.ord = Some(ord);
597        self
598    }
599
600    /// Sets the ord function for this builder if Some.
601    pub const fn ord_maybe(mut self, ord: Option<CmpFn>) -> Self {
602        self.ord = ord;
603        self
604    }
605
606    /// Sets the hash function for this builder.
607    pub const fn hash(mut self, hash: HashFn) -> Self {
608        self.hash = Some(hash);
609        self
610    }
611
612    /// Sets the hash function for this builder if Some.
613    pub const fn hash_maybe(mut self, hash: Option<HashFn>) -> Self {
614        self.hash = hash;
615        self
616    }
617
618    /// Sets the drop_in_place function for this builder.
619    pub const fn drop_in_place(mut self, drop_in_place: DropInPlaceFn) -> Self {
620        self.drop_in_place = Some(drop_in_place);
621        self
622    }
623
624    /// Sets the drop_in_place function for this builder if Some.
625    pub const fn drop_in_place_maybe(mut self, drop_in_place: Option<DropInPlaceFn>) -> Self {
626        self.drop_in_place = drop_in_place;
627        self
628    }
629
630    /// Sets the parse function for this builder.
631    pub const fn parse(mut self, parse: ParseFn) -> Self {
632        self.parse = Some(parse);
633        self
634    }
635
636    /// Sets the parse function for this builder if Some.
637    pub const fn parse_maybe(mut self, parse: Option<ParseFn>) -> Self {
638        self.parse = parse;
639        self
640    }
641
642    /// Sets the try_from function for this builder.
643    pub const fn try_from(mut self, try_from: TryFromFn) -> Self {
644        self.try_from = Some(try_from);
645        self
646    }
647
648    /// Sets the try_from function for this builder if Some.
649    pub const fn try_from_maybe(mut self, try_from: Option<TryFromFn>) -> Self {
650        self.try_from = try_from;
651        self
652    }
653
654    /// Builds the [`ValueVTable`] from the current state of the builder.
655    pub const fn build(self) -> ValueVTable {
656        ValueVTable {
657            type_name: self.type_name.unwrap(),
658            display: self.display,
659            debug: self.debug,
660            default_in_place: self.default_in_place,
661            clone_into: self.clone_into,
662            marker_traits: self.marker_traits,
663            eq: self.eq,
664            partial_ord: self.partial_ord,
665            ord: self.ord,
666            hash: self.hash,
667            drop_in_place: self.drop_in_place,
668            parse: self.parse,
669            try_from: self.try_from,
670        }
671    }
672}