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}