Skip to main content

facet_core/types/
vtable.rs

1//////////////////////////////////////////////////////////////////////
2// VTable types
3//////////////////////////////////////////////////////////////////////
4
5use crate::{OxPtrConst, OxPtrMut, OxPtrUninit, PtrMut};
6use alloc::string::String;
7use core::{cmp, fmt, hash::Hasher, marker::PhantomData, mem::transmute};
8
9//////////////////////////////////////////////////////////////////////
10// TypeNameOpts - options for formatting type names
11//////////////////////////////////////////////////////////////////////
12
13/// Options for formatting the name of a type.
14///
15/// Controls recursion depth when printing nested types like `Option<Vec<String>>`.
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 (infinite recursion).
21    pub recurse_ttl: isize,
22}
23
24impl Default for TypeNameOpts {
25    #[inline]
26    fn default() -> Self {
27        Self { recurse_ttl: -1 }
28    }
29}
30
31impl TypeNameOpts {
32    /// Create options where no type parameters are formatted (just `…`).
33    #[inline]
34    pub const fn none() -> Self {
35        Self { recurse_ttl: 0 }
36    }
37
38    /// Create options where only direct children are formatted.
39    #[inline]
40    pub const fn one() -> Self {
41        Self { recurse_ttl: 1 }
42    }
43
44    /// Create options where all type parameters are formatted (infinite depth).
45    #[inline]
46    pub const fn infinite() -> Self {
47        Self { recurse_ttl: -1 }
48    }
49
50    /// Decrease the `recurse_ttl` for child type parameters.
51    ///
52    /// Returns `None` if you should render `…` instead of type parameters.
53    /// Returns `Some(opts)` with decremented TTL to pass to children.
54    #[inline]
55    pub const fn for_children(&self) -> Option<Self> {
56        if self.recurse_ttl == 0 {
57            None
58        } else if self.recurse_ttl < 0 {
59            Some(*self)
60        } else {
61            Some(Self {
62                recurse_ttl: self.recurse_ttl - 1,
63            })
64        }
65    }
66}
67
68/// Function pointer type for formatting type names.
69///
70/// Takes the shape (for accessing type params) and formatting options.
71/// This lives on `Shape`, not in the vtable, because it's about the type itself,
72/// not about values of the type.
73pub type TypeNameFn =
74    fn(shape: &'static crate::Shape, f: &mut fmt::Formatter<'_>, opts: TypeNameOpts) -> fmt::Result;
75
76//////////////////////////////////////////////////////////////////////
77// HashProxy - Type-erased Hasher for vtable use
78//////////////////////////////////////////////////////////////////////
79
80/// A proxy type that wraps `&mut dyn Hasher` and implements `Hasher`.
81///
82/// This allows storing a concrete `Hash::hash::<HashProxy>` function pointer
83/// in the vtable, avoiding the generic `H: Hasher` parameter.
84///
85/// # Example
86///
87/// ```ignore
88/// // In vtable builder:
89/// .hash(<MyType as Hash>::hash::<HashProxy>)
90///
91/// // At call site:
92/// let mut proxy = HashProxy::new(&mut my_hasher);
93/// unsafe { (vtable.hash.unwrap())(ptr, &mut proxy) };
94/// ```
95pub struct HashProxy<'a> {
96    inner: &'a mut dyn Hasher,
97}
98
99impl<'a> HashProxy<'a> {
100    /// Create a new HashProxy wrapping a hasher.
101    #[inline]
102    pub fn new(hasher: &'a mut dyn Hasher) -> Self {
103        Self { inner: hasher }
104    }
105}
106
107impl Hasher for HashProxy<'_> {
108    #[inline]
109    fn finish(&self) -> u64 {
110        self.inner.finish()
111    }
112
113    #[inline]
114    fn write(&mut self, bytes: &[u8]) {
115        self.inner.write(bytes)
116    }
117
118    #[inline]
119    fn write_u8(&mut self, i: u8) {
120        self.inner.write_u8(i)
121    }
122
123    #[inline]
124    fn write_u16(&mut self, i: u16) {
125        self.inner.write_u16(i)
126    }
127
128    #[inline]
129    fn write_u32(&mut self, i: u32) {
130        self.inner.write_u32(i)
131    }
132
133    #[inline]
134    fn write_u64(&mut self, i: u64) {
135        self.inner.write_u64(i)
136    }
137
138    #[inline]
139    fn write_u128(&mut self, i: u128) {
140        self.inner.write_u128(i)
141    }
142
143    #[inline]
144    fn write_usize(&mut self, i: usize) {
145        self.inner.write_usize(i)
146    }
147
148    #[inline]
149    fn write_i8(&mut self, i: i8) {
150        self.inner.write_i8(i)
151    }
152
153    #[inline]
154    fn write_i16(&mut self, i: i16) {
155        self.inner.write_i16(i)
156    }
157
158    #[inline]
159    fn write_i32(&mut self, i: i32) {
160        self.inner.write_i32(i)
161    }
162
163    #[inline]
164    fn write_i64(&mut self, i: i64) {
165        self.inner.write_i64(i)
166    }
167
168    #[inline]
169    fn write_i128(&mut self, i: i128) {
170        self.inner.write_i128(i)
171    }
172
173    #[inline]
174    fn write_isize(&mut self, i: isize) {
175        self.inner.write_isize(i)
176    }
177}
178
179//////////////////////////////////////////////////////////////////////
180// VTableDirect - For concrete types
181//////////////////////////////////////////////////////////////////////
182
183/// VTable for concrete types with compile-time known traits.
184///
185/// Uses thin pointers (`*const ()`, `*mut ()`) as receivers.
186/// Used for scalars, String, user-defined structs/enums, etc.
187///
188/// ## Per-type operations
189///
190/// Note that `drop_in_place`, `default_in_place`, and `clone_into` are NOT in this struct.
191/// These operations must be monomorphized per-type and live in [`TypeOps`] on the [`crate::Shape`].
192/// This allows vtables to be shared across generic instantiations (e.g., one vtable for all `Vec<T>`).
193///
194/// ## Safety
195///
196/// All function pointers are `unsafe fn` because they operate on raw pointers.
197/// Callers must ensure:
198/// - The pointer points to a valid instance of the expected type
199/// - The pointer has the correct alignment for the type
200/// - For mutable operations, the caller has exclusive access to the data
201/// - The lifetime of the data extends for the duration of the operation
202#[allow(clippy::type_complexity)]
203#[derive(Clone, Copy)]
204pub struct VTableDirect {
205    /// Display function - formats value using Display trait.
206    pub display: Option<unsafe fn(*const (), &mut fmt::Formatter<'_>) -> fmt::Result>,
207
208    /// Debug function - formats value using Debug trait.
209    pub debug: Option<unsafe fn(*const (), &mut fmt::Formatter<'_>) -> fmt::Result>,
210
211    /// Hash function - hashes value using Hash trait via HashProxy.
212    pub hash: Option<unsafe fn(*const (), &mut HashProxy<'_>)>,
213
214    /// Invariants function - checks type invariants.
215    pub invariants: Option<unsafe fn(*const ()) -> Result<(), String>>,
216
217    /// Parse function - parses value from string into destination.
218    pub parse: Option<unsafe fn(&str, *mut ()) -> Result<(), crate::ParseError>>,
219
220    /// Parse bytes function - parses value from byte slice into destination.
221    /// Used for binary formats where types have a more efficient representation.
222    pub parse_bytes: Option<unsafe fn(&[u8], *mut ()) -> Result<(), crate::ParseError>>,
223
224    /// Try from function - converts from another value type.
225    ///
226    /// # Arguments
227    /// - `dst`: Destination pointer where the converted value will be written
228    /// - `src_shape`: Shape of the source type
229    /// - `src_ptr`: Pointer to the source value
230    ///
231    /// # Return Value
232    ///
233    /// Returns [`crate::TryFromOutcome`] which encodes both the result and ownership semantics:
234    ///
235    /// - [`crate::TryFromOutcome::Converted`]: Success. Source was consumed.
236    /// - [`crate::TryFromOutcome::Unsupported`]: Source type not supported. Source was NOT consumed.
237    /// - [`crate::TryFromOutcome::Failed`]: Conversion failed. Source WAS consumed.
238    ///
239    /// This design allows callers to attempt multiple `try_from` conversions in sequence
240    /// until one succeeds, without losing the source value on type mismatches.
241    ///
242    /// # Implementation Guidelines
243    ///
244    /// Implementations should follow this pattern:
245    /// ```ignore
246    /// // Check type BEFORE consuming - only consume supported types
247    /// if src_shape.id == <String as Facet>::SHAPE.id {
248    ///     let value = src.read::<String>();  // Consume the value
249    ///     match convert(value) {
250    ///         Ok(converted) => {
251    ///             unsafe { dst.write(converted) };
252    ///             TryFromOutcome::Converted
253    ///         }
254    ///         Err(e) => TryFromOutcome::Failed(e.into()),
255    ///     }
256    /// } else if src_shape.id == <&str as Facet>::SHAPE.id {
257    ///     // Copy types can use get() since they're trivially copyable
258    ///     let value: &str = *src.get::<&str>();
259    ///     match convert_str(value) {
260    ///         Ok(converted) => {
261    ///             unsafe { dst.write(converted) };
262    ///             TryFromOutcome::Converted
263    ///         }
264    ///         Err(e) => TryFromOutcome::Failed(e.into()),
265    ///     }
266    /// } else {
267    ///     // Unsupported type - return WITHOUT consuming
268    ///     TryFromOutcome::Unsupported
269    /// }
270    /// ```
271    ///
272    /// # Safety
273    ///
274    /// - `dst` must be valid for writes and properly aligned for the destination type
275    /// - `src_ptr` must point to valid, initialized memory of the type described by `src_shape`
276    pub try_from:
277        Option<unsafe fn(*mut (), &'static crate::Shape, crate::PtrConst) -> crate::TryFromOutcome>,
278
279    /// Try into inner function - extracts inner value (consuming).
280    pub try_into_inner: Option<unsafe fn(*mut ()) -> Result<PtrMut, String>>,
281
282    /// Try borrow inner function - borrows inner value.
283    pub try_borrow_inner: Option<unsafe fn(*const ()) -> Result<PtrMut, String>>,
284
285    /// PartialEq function - tests equality with another value.
286    pub partial_eq: Option<unsafe fn(*const (), *const ()) -> bool>,
287
288    /// PartialOrd function - compares with another value.
289    pub partial_cmp: Option<unsafe fn(*const (), *const ()) -> Option<cmp::Ordering>>,
290
291    /// Ord function - total ordering comparison.
292    pub cmp: Option<unsafe fn(*const (), *const ()) -> cmp::Ordering>,
293}
294
295impl Default for VTableDirect {
296    fn default() -> Self {
297        Self::empty()
298    }
299}
300
301impl VTableDirect {
302    /// Create an empty VTableDirect with all fields set to None.
303    pub const fn empty() -> Self {
304        Self {
305            display: None,
306            debug: None,
307            hash: None,
308            invariants: None,
309            parse: None,
310            parse_bytes: None,
311            try_from: None,
312            try_into_inner: None,
313            try_borrow_inner: None,
314            partial_eq: None,
315            partial_cmp: None,
316            cmp: None,
317        }
318    }
319
320    /// Start building a new VTableDirect (untyped).
321    pub const fn builder() -> Self {
322        Self::empty()
323    }
324}
325
326//////////////////////////////////////////////////////////////////////
327// VTableIndirect - For generic containers
328//////////////////////////////////////////////////////////////////////
329
330/// VTable for generic containers with runtime trait resolution.
331///
332/// Uses `OxPtrConst`/`OxPtrMut` as receivers to access inner type's shape at runtime.
333/// Used for `Vec<T>`, `Option<T>`, `Arc<T>`, etc.
334///
335/// Returns `Option` to indicate whether the operation is supported
336/// (the inner type may not implement the required trait).
337///
338/// ## Per-type operations
339///
340/// Note that `drop_in_place`, `default_in_place`, and `clone_into` are NOT in this struct.
341/// These operations must be monomorphized per-type and live in [`TypeOps`] on the [`crate::Shape`].
342/// This allows vtables to be shared across generic instantiations (e.g., one vtable for all `Vec<T>`).
343///
344/// ## Safety
345///
346/// All function pointers are `unsafe fn` because they operate on raw pointers.
347/// Callers must ensure:
348/// - The pointer points to a valid instance of the expected type
349/// - The pointer has the correct alignment for the type
350/// - For mutable operations, the caller has exclusive access to the data
351/// - The lifetime of the data extends for the duration of the operation
352#[allow(clippy::type_complexity)]
353#[derive(Clone, Copy)]
354pub struct VTableIndirect {
355    /// Display function - formats value using Display trait.
356    pub display: Option<unsafe fn(OxPtrConst, &mut fmt::Formatter<'_>) -> Option<fmt::Result>>,
357
358    /// Debug function - formats value using Debug trait.
359    pub debug: Option<unsafe fn(OxPtrConst, &mut fmt::Formatter<'_>) -> Option<fmt::Result>>,
360
361    /// Hash function - hashes value using Hash trait via HashProxy.
362    pub hash: Option<unsafe fn(OxPtrConst, &mut HashProxy<'_>) -> Option<()>>,
363
364    /// Invariants function - checks type invariants.
365    pub invariants: Option<unsafe fn(OxPtrConst) -> Option<Result<(), String>>>,
366
367    /// Parse function - parses value from string into uninitialized destination.
368    ///
369    /// The destination is uninitialized memory. Implementations must use `target.put(value)`
370    /// to write the parsed value, NOT `*target.as_mut() = value` which would try to drop
371    /// uninitialized memory.
372    pub parse: Option<unsafe fn(&str, OxPtrUninit) -> Option<Result<(), crate::ParseError>>>,
373
374    /// Parse bytes function - parses value from byte slice into uninitialized destination.
375    /// Used for binary formats where types have a more efficient representation.
376    ///
377    /// The destination is uninitialized memory. Implementations must use `target.put(value)`
378    /// to write the parsed value.
379    pub parse_bytes: Option<unsafe fn(&[u8], OxPtrUninit) -> Option<Result<(), crate::ParseError>>>,
380
381    /// Try from function - converts from another value type into uninitialized destination.
382    ///
383    /// # Arguments
384    /// - `dst`: Uninitialized destination pointer where the converted value will be written
385    /// - `src_shape`: Shape of the source type
386    /// - `src_ptr`: Pointer to the source value
387    ///
388    /// # Return Value
389    ///
390    /// Returns [`crate::TryFromOutcome`] which encodes both the result and ownership semantics:
391    ///
392    /// - [`crate::TryFromOutcome::Converted`]: Success. Source was consumed.
393    /// - [`crate::TryFromOutcome::Unsupported`]: Source type not supported. Source was NOT consumed.
394    /// - [`crate::TryFromOutcome::Failed`]: Conversion failed. Source WAS consumed.
395    ///
396    /// See [`VTableDirect::try_from`] for implementation patterns.
397    ///
398    /// # Safety
399    ///
400    /// - `dst` must be valid for writes and properly aligned for the destination type
401    /// - `src_ptr` must point to valid, initialized memory of the type described by `src_shape`
402    ///
403    /// # Implementation Note
404    ///
405    /// The destination is uninitialized memory. Implementations must use `dst.put(value)`
406    /// to write the converted value, NOT `*dst.as_mut() = value`.
407    pub try_from: Option<
408        unsafe fn(OxPtrUninit, &'static crate::Shape, crate::PtrConst) -> crate::TryFromOutcome,
409    >,
410
411    /// Try into inner function - extracts inner value (consuming).
412    pub try_into_inner: Option<unsafe fn(OxPtrMut) -> Option<Result<PtrMut, String>>>,
413
414    /// Try borrow inner function - borrows inner value.
415    pub try_borrow_inner: Option<unsafe fn(OxPtrConst) -> Option<Result<PtrMut, String>>>,
416
417    /// PartialEq function - tests equality with another value.
418    pub partial_eq: Option<unsafe fn(OxPtrConst, OxPtrConst) -> Option<bool>>,
419
420    /// PartialOrd function - compares with another value.
421    pub partial_cmp: Option<unsafe fn(OxPtrConst, OxPtrConst) -> Option<Option<cmp::Ordering>>>,
422
423    /// Ord function - total ordering comparison.
424    pub cmp: Option<unsafe fn(OxPtrConst, OxPtrConst) -> Option<cmp::Ordering>>,
425}
426
427impl Default for VTableIndirect {
428    fn default() -> Self {
429        Self::empty()
430    }
431}
432
433impl VTableIndirect {
434    /// An empty VTableIndirect with all fields set to None.
435    pub const EMPTY: Self = Self {
436        display: None,
437        debug: None,
438        hash: None,
439        invariants: None,
440        parse: None,
441        parse_bytes: None,
442        try_from: None,
443        try_into_inner: None,
444        try_borrow_inner: None,
445        partial_eq: None,
446        partial_cmp: None,
447        cmp: None,
448    };
449
450    /// Returns an empty VTableIndirect with all fields set to None.
451    pub const fn empty() -> Self {
452        Self::EMPTY
453    }
454}
455
456//////////////////////////////////////////////////////////////////////
457// Typed builder for VTableDirect
458//////////////////////////////////////////////////////////////////////
459
460/// Type-safe builder for VTableDirect.
461///
462/// Generic over `T` at the type level, ensuring all function pointers
463/// are for the same type. The transmute to erased pointers happens
464/// inside the builder methods.
465pub struct TypedVTableDirectBuilder<T> {
466    vtable: VTableDirect,
467    _marker: PhantomData<T>,
468}
469
470impl VTableDirect {
471    /// Create a typed builder for type T.
472    ///
473    /// The builder ensures all function pointers are for the same type T.
474    pub const fn builder_for<T>() -> TypedVTableDirectBuilder<T> {
475        TypedVTableDirectBuilder {
476            vtable: VTableDirect::empty(),
477            _marker: PhantomData,
478        }
479    }
480}
481
482impl<T> TypedVTableDirectBuilder<T> {
483    /// Set the display function.
484    pub const fn display(mut self, f: fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result) -> Self {
485        self.vtable.display = Some(unsafe {
486            transmute::<
487                fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
488                unsafe fn(*const (), &mut fmt::Formatter<'_>) -> fmt::Result,
489            >(f)
490        });
491        self
492    }
493
494    /// Set the debug function.
495    pub const fn debug(mut self, f: fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result) -> Self {
496        self.vtable.debug = Some(unsafe {
497            transmute::<
498                fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
499                unsafe fn(*const (), &mut fmt::Formatter<'_>) -> fmt::Result,
500            >(f)
501        });
502        self
503    }
504
505    /// Set the hash function.
506    ///
507    /// Pass `<T as Hash>::hash::<HashProxy>` to use the type's Hash impl.
508    pub const fn hash(mut self, f: fn(&T, &mut HashProxy<'static>)) -> Self {
509        self.vtable.hash = Some(unsafe {
510            transmute::<fn(&T, &mut HashProxy<'static>), unsafe fn(*const (), &mut HashProxy<'_>)>(
511                f,
512            )
513        });
514        self
515    }
516
517    /// Set the invariants function.
518    pub const fn invariants(mut self, f: fn(&T) -> Result<(), String>) -> Self {
519        self.vtable.invariants = Some(unsafe {
520            transmute::<fn(&T) -> Result<(), String>, unsafe fn(*const ()) -> Result<(), String>>(f)
521        });
522        self
523    }
524
525    /// Set the parse function.
526    pub const fn parse(
527        mut self,
528        f: unsafe fn(&str, *mut T) -> Result<(), crate::ParseError>,
529    ) -> Self {
530        self.vtable.parse = Some(unsafe {
531            transmute::<
532                unsafe fn(&str, *mut T) -> Result<(), crate::ParseError>,
533                unsafe fn(&str, *mut ()) -> Result<(), crate::ParseError>,
534            >(f)
535        });
536        self
537    }
538
539    /// Set the parse_bytes function.
540    ///
541    /// For types with efficient binary representations (e.g., UUID as 16 bytes).
542    pub const fn parse_bytes(
543        mut self,
544        f: unsafe fn(&[u8], *mut T) -> Result<(), crate::ParseError>,
545    ) -> Self {
546        self.vtable.parse_bytes = Some(unsafe {
547            transmute::<
548                unsafe fn(&[u8], *mut T) -> Result<(), crate::ParseError>,
549                unsafe fn(&[u8], *mut ()) -> Result<(), crate::ParseError>,
550            >(f)
551        });
552        self
553    }
554
555    /// Set the try_from function.
556    /// Arguments: (dst, src_shape, src_ptr)
557    pub const fn try_from(
558        mut self,
559        f: unsafe fn(*mut T, &'static crate::Shape, crate::PtrConst) -> crate::TryFromOutcome,
560    ) -> Self {
561        self.vtable.try_from = Some(unsafe {
562            transmute::<
563                unsafe fn(*mut T, &'static crate::Shape, crate::PtrConst) -> crate::TryFromOutcome,
564                unsafe fn(*mut (), &'static crate::Shape, crate::PtrConst) -> crate::TryFromOutcome,
565            >(f)
566        });
567        self
568    }
569
570    /// Set the try_into_inner function.
571    ///
572    /// For transparent types, this extracts the inner value (consuming).
573    /// Takes a pointer to the wrapper type, returns a pointer to the inner value.
574    pub const fn try_into_inner(mut self, f: unsafe fn(*mut T) -> Result<PtrMut, String>) -> Self {
575        self.vtable.try_into_inner = Some(unsafe {
576            transmute::<
577                unsafe fn(*mut T) -> Result<PtrMut, String>,
578                unsafe fn(*mut ()) -> Result<PtrMut, String>,
579            >(f)
580        });
581        self
582    }
583
584    /// Set the try_borrow_inner function.
585    ///
586    /// For transparent types, this borrows the inner value.
587    /// Takes a pointer to the wrapper type, returns a pointer to the inner value.
588    pub const fn try_borrow_inner(
589        mut self,
590        f: unsafe fn(*const T) -> Result<PtrMut, String>,
591    ) -> Self {
592        self.vtable.try_borrow_inner = Some(unsafe {
593            transmute::<
594                unsafe fn(*const T) -> Result<PtrMut, String>,
595                unsafe fn(*const ()) -> Result<PtrMut, String>,
596            >(f)
597        });
598        self
599    }
600
601    /// Set the partial_eq function.
602    pub const fn partial_eq(mut self, f: fn(&T, &T) -> bool) -> Self {
603        self.vtable.partial_eq = Some(unsafe {
604            transmute::<fn(&T, &T) -> bool, unsafe fn(*const (), *const ()) -> bool>(f)
605        });
606        self
607    }
608
609    /// Set the partial_cmp function.
610    pub const fn partial_cmp(mut self, f: fn(&T, &T) -> Option<cmp::Ordering>) -> Self {
611        self.vtable.partial_cmp = Some(unsafe {
612            transmute::<
613                fn(&T, &T) -> Option<cmp::Ordering>,
614                unsafe fn(*const (), *const ()) -> Option<cmp::Ordering>,
615            >(f)
616        });
617        self
618    }
619
620    /// Set the cmp function.
621    pub const fn cmp(mut self, f: fn(&T, &T) -> cmp::Ordering) -> Self {
622        self.vtable.cmp = Some(unsafe {
623            transmute::<fn(&T, &T) -> cmp::Ordering, unsafe fn(*const (), *const ()) -> cmp::Ordering>(
624                f,
625            )
626        });
627        self
628    }
629
630    // Optional variants for auto-detection via `impls!` macro
631
632    /// Set the display function from an Option.
633    /// Used for auto-detection where the function may not be available.
634    pub const fn display_opt(
635        mut self,
636        f: Option<fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result>,
637    ) -> Self {
638        self.vtable.display = match f {
639            Some(f) => Some(unsafe {
640                transmute::<
641                    fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
642                    unsafe fn(*const (), &mut fmt::Formatter<'_>) -> fmt::Result,
643                >(f)
644            }),
645            None => None,
646        };
647        self
648    }
649
650    /// Set the debug function from an Option.
651    /// Used for auto-detection where the function may not be available.
652    pub const fn debug_opt(
653        mut self,
654        f: Option<fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result>,
655    ) -> Self {
656        self.vtable.debug = match f {
657            Some(f) => Some(unsafe {
658                transmute::<
659                    fn(&T, &mut fmt::Formatter<'_>) -> fmt::Result,
660                    unsafe fn(*const (), &mut fmt::Formatter<'_>) -> fmt::Result,
661                >(f)
662            }),
663            None => None,
664        };
665        self
666    }
667
668    /// Set the hash function from an Option.
669    /// Used for auto-detection where the function may not be available.
670    pub const fn hash_opt(mut self, f: Option<fn(&T, &mut HashProxy<'static>)>) -> Self {
671        self.vtable.hash = match f {
672            Some(f) => Some(unsafe {
673                transmute::<fn(&T, &mut HashProxy<'static>), unsafe fn(*const (), &mut HashProxy<'_>)>(
674                    f,
675                )
676            }),
677            None => None,
678        };
679        self
680    }
681
682    /// Set the partial_eq function from an Option.
683    /// Used for auto-detection where the function may not be available.
684    pub const fn partial_eq_opt(mut self, f: Option<fn(&T, &T) -> bool>) -> Self {
685        self.vtable.partial_eq = match f {
686            Some(f) => Some(unsafe {
687                transmute::<fn(&T, &T) -> bool, unsafe fn(*const (), *const ()) -> bool>(f)
688            }),
689            None => None,
690        };
691        self
692    }
693
694    /// Set the partial_cmp function from an Option.
695    /// Used for auto-detection where the function may not be available.
696    pub const fn partial_cmp_opt(mut self, f: Option<fn(&T, &T) -> Option<cmp::Ordering>>) -> Self {
697        self.vtable.partial_cmp = match f {
698            Some(f) => Some(unsafe {
699                transmute::<
700                    fn(&T, &T) -> Option<cmp::Ordering>,
701                    unsafe fn(*const (), *const ()) -> Option<cmp::Ordering>,
702                >(f)
703            }),
704            None => None,
705        };
706        self
707    }
708
709    /// Set the cmp function from an Option.
710    /// Used for auto-detection where the function may not be available.
711    pub const fn cmp_opt(mut self, f: Option<fn(&T, &T) -> cmp::Ordering>) -> Self {
712        self.vtable.cmp = match f {
713            Some(f) => Some(unsafe {
714                transmute::<
715                    fn(&T, &T) -> cmp::Ordering,
716                    unsafe fn(*const (), *const ()) -> cmp::Ordering,
717                >(f)
718            }),
719            None => None,
720        };
721        self
722    }
723
724    /// Build the VTable.
725    pub const fn build(self) -> VTableDirect {
726        self.vtable
727    }
728}
729
730// VTableIndirect uses struct literals directly - no builder needed
731
732//////////////////////////////////////////////////////////////////////
733// VTableErased
734//////////////////////////////////////////////////////////////////////
735
736/// Type-erased VTable that can hold either Direct or Indirect style.
737///
738/// | Variant | Use Case |
739/// |---------|----------|
740/// | Direct | Concrete types: scalars, String, derived types |
741/// | Indirect | Generic containers: `Vec<T>`, `Option<T>`, `Arc<T>` |
742#[derive(Clone, Copy)]
743pub enum VTableErased {
744    /// For concrete types with compile-time known traits.
745    Direct(&'static VTableDirect),
746
747    /// For generic containers with runtime trait resolution.
748    Indirect(&'static VTableIndirect),
749}
750
751impl From<&'static VTableDirect> for VTableErased {
752    fn from(vt: &'static VTableDirect) -> Self {
753        VTableErased::Direct(vt)
754    }
755}
756
757impl From<&'static VTableIndirect> for VTableErased {
758    fn from(vt: &'static VTableIndirect) -> Self {
759        VTableErased::Indirect(vt)
760    }
761}
762
763impl fmt::Debug for VTableErased {
764    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
765        match self {
766            VTableErased::Direct(_) => f.debug_tuple("Direct").field(&"...").finish(),
767            VTableErased::Indirect(_) => f.debug_tuple("Indirect").field(&"...").finish(),
768        }
769    }
770}
771
772impl VTableErased {
773    /// Check if this vtable has a display function.
774    #[inline]
775    pub const fn has_display(&self) -> bool {
776        match self {
777            VTableErased::Direct(vt) => vt.display.is_some(),
778            VTableErased::Indirect(vt) => vt.display.is_some(),
779        }
780    }
781
782    /// Check if this vtable has a debug function.
783    #[inline]
784    pub const fn has_debug(&self) -> bool {
785        match self {
786            VTableErased::Direct(vt) => vt.debug.is_some(),
787            VTableErased::Indirect(vt) => vt.debug.is_some(),
788        }
789    }
790
791    /// Check if this vtable has a hash function.
792    #[inline]
793    pub const fn has_hash(&self) -> bool {
794        match self {
795            VTableErased::Direct(vt) => vt.hash.is_some(),
796            VTableErased::Indirect(vt) => vt.hash.is_some(),
797        }
798    }
799
800    /// Check if this vtable has a partial_eq function.
801    #[inline]
802    pub const fn has_partial_eq(&self) -> bool {
803        match self {
804            VTableErased::Direct(vt) => vt.partial_eq.is_some(),
805            VTableErased::Indirect(vt) => vt.partial_eq.is_some(),
806        }
807    }
808
809    /// Check if this vtable has a partial_cmp function.
810    #[inline]
811    pub const fn has_partial_ord(&self) -> bool {
812        match self {
813            VTableErased::Direct(vt) => vt.partial_cmp.is_some(),
814            VTableErased::Indirect(vt) => vt.partial_cmp.is_some(),
815        }
816    }
817
818    /// Check if this vtable has a cmp function.
819    #[inline]
820    pub const fn has_ord(&self) -> bool {
821        match self {
822            VTableErased::Direct(vt) => vt.cmp.is_some(),
823            VTableErased::Indirect(vt) => vt.cmp.is_some(),
824        }
825    }
826
827    /// Check if this vtable has a parse function.
828    #[inline]
829    pub const fn has_parse(&self) -> bool {
830        match self {
831            VTableErased::Direct(vt) => vt.parse.is_some(),
832            VTableErased::Indirect(vt) => vt.parse.is_some(),
833        }
834    }
835
836    /// Check if this vtable has a parse_bytes function.
837    #[inline]
838    pub const fn has_parse_bytes(&self) -> bool {
839        match self {
840            VTableErased::Direct(vt) => vt.parse_bytes.is_some(),
841            VTableErased::Indirect(vt) => vt.parse_bytes.is_some(),
842        }
843    }
844
845    /// Check if this vtable has a try_from function.
846    #[inline]
847    pub const fn has_try_from(&self) -> bool {
848        match self {
849            VTableErased::Direct(vt) => vt.try_from.is_some(),
850            VTableErased::Indirect(vt) => vt.try_from.is_some(),
851        }
852    }
853
854    /// Check if this vtable has a try_borrow_inner function.
855    #[inline]
856    pub const fn has_try_borrow_inner(&self) -> bool {
857        match self {
858            VTableErased::Direct(vt) => vt.try_borrow_inner.is_some(),
859            VTableErased::Indirect(vt) => vt.try_borrow_inner.is_some(),
860        }
861    }
862
863    /// Check if this vtable has an invariants function.
864    #[inline]
865    pub const fn has_invariants(&self) -> bool {
866        match self {
867            VTableErased::Direct(vt) => vt.invariants.is_some(),
868            VTableErased::Indirect(vt) => vt.invariants.is_some(),
869        }
870    }
871}
872
873//////////////////////////////////////////////////////////////////////
874// vtable_direct! macro
875//////////////////////////////////////////////////////////////////////
876
877/// Creates a VTableDirect for a type by specifying which traits it implements.
878///
879/// Note: `drop_in_place`, `default_in_place`, and `clone_into` are NOT set by this macro.
880/// These per-type operations belong in [`TypeOps`] on the [`crate::Shape`], which allows
881/// vtables to be shared across generic instantiations.
882///
883/// Standard traits (auto-generated from trait method references):
884/// - `Display` -> `.display(<T as Display>::fmt)`
885/// - `Debug` -> `.debug(<T as Debug>::fmt)`
886/// - `Hash` -> `.hash(<T as Hash>::hash::<HashProxy>)`
887/// - `PartialEq` -> `.partial_eq(<T as PartialEq>::eq)`
888/// - `PartialOrd` -> `.partial_cmp(<T as PartialOrd>::partial_cmp)`
889/// - `Ord` -> `.cmp(<T as Ord>::cmp)`
890/// - `FromStr` -> `.parse(...)` (generates a parse function)
891///
892/// Custom functions (passed directly):
893/// - `[parse = fn_name]`
894/// - `[invariants = fn_name]`
895/// - `[try_from = fn_name]`
896/// - `[try_into_inner = fn_name]`
897/// - `[try_borrow_inner = fn_name]`
898///
899/// # Example
900///
901/// ```ignore
902/// const VTABLE: VTableDirect = vtable_direct!(char =>
903///     [parse = parse],
904///     Display,
905///     Debug,
906///     Hash,
907///     PartialEq,
908///     PartialOrd,
909///     Ord,
910/// );
911/// ```
912#[macro_export]
913macro_rules! vtable_direct {
914    // TT-muncher: start
915    ($ty:ty => $($rest:tt)*) => {
916        $crate::vtable_direct!(@build $ty, $crate::VTableDirect::builder_for::<$ty>(), $($rest)*)
917    };
918
919    // Base case: no more tokens - just build
920    (@build $ty:ty, $builder:expr, $(,)?) => {
921        $builder.build()
922    };
923
924    // Standard traits
925    (@build $ty:ty, $builder:expr, Display $(, $($rest:tt)*)?) => {
926        $crate::vtable_direct!(@build $ty, $builder.display(<$ty as core::fmt::Display>::fmt) $(, $($rest)*)?)
927    };
928    (@build $ty:ty, $builder:expr, Debug $(, $($rest:tt)*)?) => {
929        $crate::vtable_direct!(@build $ty, $builder.debug(<$ty as core::fmt::Debug>::fmt) $(, $($rest)*)?)
930    };
931    (@build $ty:ty, $builder:expr, Hash $(, $($rest:tt)*)?) => {
932        $crate::vtable_direct!(@build $ty, $builder.hash(<$ty as core::hash::Hash>::hash::<$crate::HashProxy>) $(, $($rest)*)?)
933    };
934    (@build $ty:ty, $builder:expr, PartialEq, $($rest:tt)*) => {
935        $crate::vtable_direct!(@build $ty, $builder.partial_eq(<$ty as PartialEq>::eq), $($rest)*)
936    };
937    (@build $ty:ty, $builder:expr, PartialEq $(,)?) => {
938        $crate::vtable_direct!(@build $ty, $builder.partial_eq(<$ty as PartialEq>::eq),)
939    };
940    (@build $ty:ty, $builder:expr, PartialOrd $(, $($rest:tt)*)?) => {
941        $crate::vtable_direct!(@build $ty, $builder.partial_cmp(<$ty as PartialOrd>::partial_cmp) $(, $($rest)*)?)
942    };
943    (@build $ty:ty, $builder:expr, Ord $(, $($rest:tt)*)?) => {
944        $crate::vtable_direct!(@build $ty, $builder.cmp(<$ty as Ord>::cmp) $(, $($rest)*)?)
945    };
946
947    // FromStr trait - generates parse function
948    // Note: We use a static string for the error since parse error types
949    // don't implement Facet. In the future, we could add Facet impls for them.
950    (@build $ty:ty, $builder:expr, FromStr $(, $($rest:tt)*)?) => {
951        $crate::vtable_direct!(@build $ty, $builder.parse({
952            /// # Safety
953            /// `dst` must be valid for writes and properly aligned
954            unsafe fn parse(s: &str, dst: *mut $ty) -> Result<(), $crate::ParseError> {
955                match s.parse::<$ty>() {
956                    Ok(value) => {
957                        unsafe { dst.write(value) };
958                        Ok(())
959                    }
960                    Err(_) => Err($crate::ParseError::from_str(
961                        const { concat!("failed to parse ", stringify!($ty)) }
962                    )),
963                }
964            }
965            parse
966        }) $(, $($rest)*)?)
967    };
968
969    // Custom functions - use [name = expr] syntax
970    (@build $ty:ty, $builder:expr, [parse = $f:expr] $(, $($rest:tt)*)?) => {
971        $crate::vtable_direct!(@build $ty, $builder.parse($f) $(, $($rest)*)?)
972    };
973    (@build $ty:ty, $builder:expr, [invariants = $f:expr] $(, $($rest:tt)*)?) => {
974        $crate::vtable_direct!(@build $ty, $builder.invariants($f) $(, $($rest)*)?)
975    };
976    (@build $ty:ty, $builder:expr, [try_from = $f:expr] $(, $($rest:tt)*)?) => {
977        $crate::vtable_direct!(@build $ty, $builder.try_from($f) $(, $($rest)*)?)
978    };
979    (@build $ty:ty, $builder:expr, [try_into_inner = $f:expr] $(, $($rest:tt)*)?) => {
980        $crate::vtable_direct!(@build $ty, $builder.try_into_inner($f) $(, $($rest)*)?)
981    };
982    (@build $ty:ty, $builder:expr, [try_borrow_inner = $f:expr] $(, $($rest:tt)*)?) => {
983        $crate::vtable_direct!(@build $ty, $builder.try_borrow_inner($f) $(, $($rest)*)?)
984    };
985}
986
987//////////////////////////////////////////////////////////////////////
988// vtable_indirect! macro
989//////////////////////////////////////////////////////////////////////
990
991/// Creates a VTableIndirect for generic container types by specifying which traits it implements.
992///
993/// Note: `drop_in_place`, `default_in_place`, and `clone_into` are NOT set by this macro.
994/// These per-type operations belong in [`TypeOps`] on the [`crate::Shape`], which allows
995/// vtables to be shared across generic instantiations.
996///
997/// This macro generates wrapper functions that:
998/// 1. Extract `&T` from `OxPtrConst` via `ox.get::<T>()`
999/// 2. Call the trait method
1000/// 3. Wrap the result in `Some(...)`
1001///
1002/// ## Standard traits
1003///
1004/// - `Display` -> generates display fn calling `<T as Display>::fmt`
1005/// - `Debug` -> generates debug fn calling `<T as Debug>::fmt`
1006/// - `Hash` -> generates hash fn calling `<T as Hash>::hash`
1007/// - `PartialEq` -> generates partial_eq fn calling `<T as PartialEq>::eq`
1008/// - `PartialOrd` -> generates partial_cmp fn calling `<T as PartialOrd>::partial_cmp`
1009/// - `Ord` -> generates cmp fn calling `<T as Ord>::cmp`
1010///
1011/// ## Example
1012///
1013/// ```ignore
1014/// // Simple usage with standard traits
1015/// const VTABLE: VTableIndirect = vtable_indirect!(std::path::Path =>
1016///     Debug,
1017///     Hash,
1018///     PartialEq,
1019///     PartialOrd,
1020///     Ord,
1021/// );
1022/// ```
1023#[macro_export]
1024macro_rules! vtable_indirect {
1025    // Entry point - process traits one at a time
1026    ($ty:ty => $($traits:ident),* $(,)?) => {{
1027        $crate::VTableIndirect {
1028            display: $crate::vtable_indirect!(@display $ty; $($traits),*),
1029            debug: $crate::vtable_indirect!(@debug $ty; $($traits),*),
1030            hash: $crate::vtable_indirect!(@hash $ty; $($traits),*),
1031            invariants: None,
1032            parse: None,
1033            parse_bytes: None,
1034            try_from: None,
1035            try_into_inner: None,
1036            try_borrow_inner: None,
1037            partial_eq: $crate::vtable_indirect!(@partial_eq $ty; $($traits),*),
1038            partial_cmp: $crate::vtable_indirect!(@partial_cmp $ty; $($traits),*),
1039            cmp: $crate::vtable_indirect!(@cmp $ty; $($traits),*),
1040        }
1041    }};
1042
1043    // Display - match or None
1044    (@display $ty:ty; Display $(, $($rest:ident),*)?) => {
1045        Some({
1046            unsafe fn display(ox: $crate::OxPtrConst, f: &mut core::fmt::Formatter<'_>) -> Option<core::fmt::Result> {
1047                let v: &$ty = unsafe { ox.ptr().get::<$ty>() };
1048                Some(<$ty as core::fmt::Display>::fmt(v, f))
1049            }
1050            display
1051        })
1052    };
1053    (@display $ty:ty; $other:ident $(, $($rest:ident),*)?) => {
1054        $crate::vtable_indirect!(@display $ty; $($($rest),*)?)
1055    };
1056    (@display $ty:ty;) => { None };
1057
1058    // Debug - match or None
1059    (@debug $ty:ty; Debug $(, $($rest:ident),*)?) => {
1060        Some({
1061            unsafe fn debug(ox: $crate::OxPtrConst, f: &mut core::fmt::Formatter<'_>) -> Option<core::fmt::Result> {
1062                let v: &$ty = unsafe { ox.ptr().get::<$ty>() };
1063                Some(<$ty as core::fmt::Debug>::fmt(v, f))
1064            }
1065            debug
1066        })
1067    };
1068    (@debug $ty:ty; $other:ident $(, $($rest:ident),*)?) => {
1069        $crate::vtable_indirect!(@debug $ty; $($($rest),*)?)
1070    };
1071    (@debug $ty:ty;) => { None };
1072
1073    // Hash - match or None
1074    (@hash $ty:ty; Hash $(, $($rest:ident),*)?) => {
1075        Some({
1076            unsafe fn hash(ox: $crate::OxPtrConst, hasher: &mut $crate::HashProxy<'_>) -> Option<()> {
1077                let v: &$ty = unsafe { ox.ptr().get::<$ty>() };
1078                <$ty as core::hash::Hash>::hash(v, hasher);
1079                Some(())
1080            }
1081            hash
1082        })
1083    };
1084    (@hash $ty:ty; $other:ident $(, $($rest:ident),*)?) => {
1085        $crate::vtable_indirect!(@hash $ty; $($($rest),*)?)
1086    };
1087    (@hash $ty:ty;) => { None };
1088
1089    // PartialEq - match or None
1090    (@partial_eq $ty:ty; PartialEq $(, $($rest:ident),*)?) => {
1091        Some({
1092            unsafe fn partial_eq(a: $crate::OxPtrConst, b: $crate::OxPtrConst) -> Option<bool> {
1093                let a: &$ty = unsafe { a.ptr().get::<$ty>() };
1094                let b: &$ty = unsafe { b.ptr().get::<$ty>() };
1095                Some(<$ty as PartialEq>::eq(a, b))
1096            }
1097            partial_eq
1098        })
1099    };
1100    (@partial_eq $ty:ty; $other:ident $(, $($rest:ident),*)?) => {
1101        $crate::vtable_indirect!(@partial_eq $ty; $($($rest),*)?)
1102    };
1103    (@partial_eq $ty:ty;) => { None };
1104
1105    // PartialOrd - match or None
1106    (@partial_cmp $ty:ty; PartialOrd $(, $($rest:ident),*)?) => {
1107        Some({
1108            unsafe fn partial_cmp(a: $crate::OxPtrConst, b: $crate::OxPtrConst) -> Option<Option<core::cmp::Ordering>> {
1109                let a: &$ty = unsafe { a.ptr().get::<$ty>() };
1110                let b: &$ty = unsafe { b.ptr().get::<$ty>() };
1111                Some(<$ty as PartialOrd>::partial_cmp(a, b))
1112            }
1113            partial_cmp
1114        })
1115    };
1116    (@partial_cmp $ty:ty; $other:ident $(, $($rest:ident),*)?) => {
1117        $crate::vtable_indirect!(@partial_cmp $ty; $($($rest),*)?)
1118    };
1119    (@partial_cmp $ty:ty;) => { None };
1120
1121    // Ord - match or None
1122    (@cmp $ty:ty; Ord $(, $($rest:ident),*)?) => {
1123        Some({
1124            unsafe fn cmp(a: $crate::OxPtrConst, b: $crate::OxPtrConst) -> Option<core::cmp::Ordering> {
1125                let a: &$ty = unsafe { a.ptr().get::<$ty>() };
1126                let b: &$ty = unsafe { b.ptr().get::<$ty>() };
1127                Some(<$ty as Ord>::cmp(a, b))
1128            }
1129            cmp
1130        })
1131    };
1132    (@cmp $ty:ty; $other:ident $(, $($rest:ident),*)?) => {
1133        $crate::vtable_indirect!(@cmp $ty; $($($rest),*)?)
1134    };
1135    (@cmp $ty:ty;) => { None };
1136}
1137
1138//////////////////////////////////////////////////////////////////////
1139// Type aliases for macro-generated attribute code
1140//////////////////////////////////////////////////////////////////////
1141
1142/// Function type for default initialization in-place.
1143/// Used by the `#[facet(default)]` attribute.
1144pub type DefaultInPlaceFn = unsafe fn(target: crate::PtrUninit) -> crate::PtrMut;
1145
1146/// Function type for type invariant validation.
1147/// Used by the `#[facet(invariants = fn)]` attribute.
1148pub type InvariantsFn = unsafe fn(value: crate::PtrConst) -> bool;
1149
1150/// Function type for truthiness checks used by skip_unless_truthy-style helpers.
1151pub type TruthyFn = unsafe fn(value: crate::PtrConst) -> bool;
1152
1153//////////////////////////////////////////////////////////////////////
1154// type_ops_direct! macro
1155//////////////////////////////////////////////////////////////////////
1156
1157/// Creates a TypeOpsDirect for a type by specifying which traits it implements.
1158///
1159/// ## Supported traits
1160///
1161/// - `Default` -> generates default_in_place function
1162/// - `Clone` -> generates clone_into function
1163///
1164/// Note: `drop_in_place` is always generated automatically using `core::ptr::drop_in_place`.
1165///
1166/// ## Example
1167///
1168/// ```ignore
1169/// const TYPE_OPS: TypeOpsDirect = type_ops_direct!(u32 => Default, Clone);
1170/// ```
1171#[macro_export]
1172macro_rules! type_ops_direct {
1173    // Neither Default nor Clone
1174    ($ty:ty =>) => {{
1175        #[allow(clippy::useless_transmute)]
1176        $crate::TypeOpsDirect {
1177            drop_in_place: unsafe { core::mem::transmute::<unsafe fn(*mut $ty), unsafe fn(*mut ())>(core::ptr::drop_in_place::<$ty>) },
1178            default_in_place: None,
1179            clone_into: None,
1180            is_truthy: None,
1181        }
1182    }};
1183
1184    // Default only
1185    ($ty:ty => Default $(,)?) => {{
1186        #[allow(clippy::useless_transmute)]
1187        $crate::TypeOpsDirect {
1188            drop_in_place: unsafe { core::mem::transmute::<unsafe fn(*mut $ty), unsafe fn(*mut ())>(core::ptr::drop_in_place::<$ty>) },
1189            default_in_place: Some(unsafe { core::mem::transmute::<unsafe fn(*mut $ty), unsafe fn(*mut ())>($crate::𝟋::𝟋default_for::<$ty>()) }),
1190            clone_into: None,
1191            is_truthy: None,
1192        }
1193    }};
1194
1195    // Clone only
1196    ($ty:ty => Clone $(,)?) => {{
1197        #[allow(clippy::useless_transmute)]
1198        $crate::TypeOpsDirect {
1199            drop_in_place: unsafe { core::mem::transmute::<unsafe fn(*mut $ty), unsafe fn(*mut ())>(core::ptr::drop_in_place::<$ty>) },
1200            default_in_place: None,
1201            clone_into: Some(unsafe { core::mem::transmute::<unsafe fn(*const $ty, *mut $ty), unsafe fn(*const (), *mut ())>($crate::𝟋::𝟋clone_for::<$ty>()) }),
1202            is_truthy: None,
1203        }
1204    }};
1205
1206    // Both Default and Clone (either order)
1207    ($ty:ty => Default, Clone $(,)?) => {{
1208        #[allow(clippy::useless_transmute)]
1209        $crate::TypeOpsDirect {
1210            drop_in_place: unsafe { core::mem::transmute::<unsafe fn(*mut $ty), unsafe fn(*mut ())>(core::ptr::drop_in_place::<$ty>) },
1211            default_in_place: Some(unsafe { core::mem::transmute::<unsafe fn(*mut $ty), unsafe fn(*mut ())>($crate::𝟋::𝟋default_for::<$ty>()) }),
1212            clone_into: Some(unsafe { core::mem::transmute::<unsafe fn(*const $ty, *mut $ty), unsafe fn(*const (), *mut ())>($crate::𝟋::𝟋clone_for::<$ty>()) }),
1213            is_truthy: None,
1214        }
1215    }};
1216
1217    ($ty:ty => Clone, Default $(,)?) => {
1218        $crate::type_ops_direct!($ty => Default, Clone)
1219    };
1220}
1221
1222//////////////////////////////////////////////////////////////////////
1223// TypeOps - Per-type operations that must be monomorphized
1224//////////////////////////////////////////////////////////////////////
1225
1226/// Type-specific operations for concrete types (uses thin pointers).
1227///
1228/// Used for scalars, String, user-defined structs/enums, etc.
1229///
1230/// These operations must be monomorphized per-type because they need to know
1231/// the concrete type `T` at compile time:
1232/// - `drop_in_place`: Needs to call `T`'s destructor
1233/// - `default_in_place`: Needs to construct `T::default()`
1234/// - `clone_into`: Needs to call `T::clone()`
1235#[derive(Clone, Copy, Debug)]
1236#[repr(C)]
1237pub struct TypeOpsDirect {
1238    /// Drop the value in place.
1239    ///
1240    /// # Safety
1241    /// The pointer must point to a valid, initialized value of the correct type.
1242    pub drop_in_place: unsafe fn(*mut ()),
1243
1244    /// Construct a default value in place.
1245    ///
1246    /// Returns `None` if the type doesn't implement `Default`.
1247    ///
1248    /// # Safety
1249    /// The pointer must point to uninitialized memory of sufficient size and alignment.
1250    pub default_in_place: Option<unsafe fn(*mut ())>,
1251
1252    /// Clone a value into uninitialized memory.
1253    ///
1254    /// Returns `None` if the type doesn't implement `Clone`.
1255    ///
1256    /// # Safety
1257    /// - `src` must point to a valid, initialized value
1258    /// - `dst` must point to uninitialized memory of sufficient size and alignment
1259    pub clone_into: Option<unsafe fn(src: *const (), dst: *mut ())>,
1260
1261    /// Truthiness predicate for this type. When absent, the type is never considered truthy.
1262    pub is_truthy: Option<TruthyFn>,
1263}
1264
1265// TypeOpsDirect uses struct literals directly - no builder needed
1266
1267/// Type-specific operations for generic containers (uses wide pointers with shape).
1268///
1269/// Used for `Vec<T>`, `Option<T>`, `Arc<T>`, etc.
1270///
1271/// These operations must be monomorphized per-type because they need to know
1272/// the concrete type `T` at compile time.
1273#[derive(Clone, Copy, Debug)]
1274#[repr(C)]
1275pub struct TypeOpsIndirect {
1276    /// Drop the value in place.
1277    ///
1278    /// # Safety
1279    /// The pointer must point to a valid, initialized value of the correct type.
1280    pub drop_in_place: unsafe fn(OxPtrMut),
1281
1282    /// Construct a default value in place.
1283    ///
1284    /// The outer `Option` is `None` if the type doesn't implement `Default`.
1285    /// The function returns `true` on success, `false` if initialization failed
1286    /// (e.g., for arrays where an element type doesn't support Default).
1287    ///
1288    /// # Safety
1289    /// The pointer must point to uninitialized memory of sufficient size and alignment.
1290    pub default_in_place: Option<unsafe fn(OxPtrUninit) -> bool>,
1291
1292    /// Clone a value into uninitialized memory.
1293    ///
1294    /// Returns `None` if the type doesn't implement `Clone`.
1295    ///
1296    /// # Safety
1297    /// - `src` must point to a valid, initialized value
1298    /// - `dst` must point to uninitialized memory of sufficient size and alignment
1299    pub clone_into: Option<unsafe fn(src: OxPtrConst, dst: OxPtrMut)>,
1300
1301    /// Truthiness predicate for this type. When absent, the type is never considered truthy.
1302    pub is_truthy: Option<TruthyFn>,
1303}
1304
1305// TypeOpsIndirect uses struct literals directly - no builder needed
1306
1307/// Type-erased TypeOps that can hold either Direct or Indirect style.
1308///
1309/// | Variant | Use Case |
1310/// |---------|----------|
1311/// | Direct | Concrete types: scalars, String, derived types |
1312/// | Indirect | Generic containers: `Vec<T>`, `Option<T>`, `Arc<T>` |
1313#[derive(Clone, Copy, Debug)]
1314pub enum TypeOps {
1315    /// For concrete types with thin pointers.
1316    Direct(&'static TypeOpsDirect),
1317
1318    /// For generic containers with wide pointers (includes shape).
1319    Indirect(&'static TypeOpsIndirect),
1320}
1321
1322impl From<&'static TypeOpsDirect> for TypeOps {
1323    fn from(ops: &'static TypeOpsDirect) -> Self {
1324        TypeOps::Direct(ops)
1325    }
1326}
1327
1328impl From<&'static TypeOpsIndirect> for TypeOps {
1329    fn from(ops: &'static TypeOpsIndirect) -> Self {
1330        TypeOps::Indirect(ops)
1331    }
1332}
1333
1334impl TypeOps {
1335    /// Check if this type has a clone_into operation.
1336    #[inline]
1337    pub const fn has_clone_into(&self) -> bool {
1338        match self {
1339            TypeOps::Direct(ops) => ops.clone_into.is_some(),
1340            TypeOps::Indirect(ops) => ops.clone_into.is_some(),
1341        }
1342    }
1343
1344    /// Check if this type has a default_in_place operation.
1345    #[inline]
1346    pub const fn has_default_in_place(&self) -> bool {
1347        match self {
1348            TypeOps::Direct(ops) => ops.default_in_place.is_some(),
1349            TypeOps::Indirect(ops) => ops.default_in_place.is_some(),
1350        }
1351    }
1352
1353    /// Returns the truthiness predicate for this type, if any.
1354    #[inline]
1355    pub const fn truthiness_fn(&self) -> Option<TruthyFn> {
1356        match self {
1357            TypeOps::Direct(ops) => ops.is_truthy,
1358            TypeOps::Indirect(ops) => ops.is_truthy,
1359        }
1360    }
1361}