facet_core/impls/core/
tuple.rs

1use core::{cmp::Ordering, fmt, mem};
2
3use crate::{
4    Def, Facet, FieldBuilder, HashProxy, OxPtrConst, OxPtrMut, OxRef, PtrConst, PtrMut, Repr,
5    Shape, ShapeBuilder, StructKind, StructType, Type, TypeNameOpts, TypeOpsIndirect, UserType,
6    VTableIndirect,
7};
8
9/// Debug for tuples - formats as tuple literal
10unsafe fn tuple_debug(
11    ox: OxPtrConst,
12    f: &mut core::fmt::Formatter<'_>,
13) -> Option<core::fmt::Result> {
14    let shape = ox.shape();
15    let ty = match shape.ty {
16        Type::User(UserType::Struct(ref st)) => st,
17        _ => return None,
18    };
19
20    let ptr = ox.ptr();
21    let mut tuple = f.debug_tuple("");
22
23    for field in ty.fields {
24        let field_ptr = unsafe { PtrConst::new(ptr.as_byte_ptr().add(field.offset)) };
25        let field_ox = OxRef::new(field_ptr, field.shape.get());
26        tuple.field(&field_ox);
27    }
28
29    Some(tuple.finish())
30}
31
32/// Hash for tuples - hashes each element
33unsafe fn tuple_hash(ox: OxPtrConst, hasher: &mut HashProxy<'_>) -> Option<()> {
34    let shape = ox.shape();
35    let ty = match shape.ty {
36        Type::User(UserType::Struct(ref st)) => st,
37        _ => return None,
38    };
39
40    let ptr = ox.ptr();
41
42    for field in ty.fields {
43        let field_ptr = unsafe { PtrConst::new(ptr.as_byte_ptr().add(field.offset)) };
44        let field_shape = field.shape.get();
45        unsafe { field_shape.call_hash(field_ptr, hasher)? };
46    }
47
48    Some(())
49}
50
51/// PartialEq for tuples
52unsafe fn tuple_partial_eq(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
53    let shape = a.shape();
54    let ty = match shape.ty {
55        Type::User(UserType::Struct(ref st)) => st,
56        _ => return None,
57    };
58
59    let a_ptr = a.ptr();
60    let b_ptr = b.ptr();
61
62    for field in ty.fields {
63        let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
64        let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
65        let field_shape = field.shape.get();
66        if !unsafe { field_shape.call_partial_eq(a_field, b_field)? } {
67            return Some(false);
68        }
69    }
70
71    Some(true)
72}
73
74/// PartialOrd for tuples
75unsafe fn tuple_partial_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Option<Ordering>> {
76    let shape = a.shape();
77    let ty = match shape.ty {
78        Type::User(UserType::Struct(ref st)) => st,
79        _ => return None,
80    };
81
82    let a_ptr = a.ptr();
83    let b_ptr = b.ptr();
84
85    for field in ty.fields {
86        let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
87        let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
88        let field_shape = field.shape.get();
89        match unsafe { field_shape.call_partial_cmp(a_field, b_field)? } {
90            Some(Ordering::Equal) => continue,
91            other => return Some(other),
92        }
93    }
94
95    Some(Some(Ordering::Equal))
96}
97
98/// Ord for tuples
99unsafe fn tuple_cmp(a: OxPtrConst, b: OxPtrConst) -> Option<Ordering> {
100    let shape = a.shape();
101    let ty = match shape.ty {
102        Type::User(UserType::Struct(ref st)) => st,
103        _ => return None,
104    };
105
106    let a_ptr = a.ptr();
107    let b_ptr = b.ptr();
108
109    for field in ty.fields {
110        let a_field = unsafe { PtrConst::new(a_ptr.as_byte_ptr().add(field.offset)) };
111        let b_field = unsafe { PtrConst::new(b_ptr.as_byte_ptr().add(field.offset)) };
112        let field_shape = field.shape.get();
113        match unsafe { field_shape.call_cmp(a_field, b_field)? } {
114            Ordering::Equal => continue,
115            other => return Some(other),
116        }
117    }
118
119    Some(Ordering::Equal)
120}
121
122/// Drop for tuples
123unsafe fn tuple_drop(ox: OxPtrMut) {
124    let shape = ox.shape();
125    let ty = match shape.ty {
126        Type::User(UserType::Struct(ref st)) => st,
127        _ => return,
128    };
129
130    let ptr = ox.ptr();
131
132    for field in ty.fields {
133        let field_ptr = unsafe { PtrMut::new((ptr.as_byte_ptr() as *mut u8).add(field.offset)) };
134        let field_shape = field.shape.get();
135        unsafe { field_shape.call_drop_in_place(field_ptr) };
136    }
137}
138
139// Shared vtable for all tuples
140const TUPLE_VTABLE: VTableIndirect = VTableIndirect {
141    display: None,
142    debug: Some(tuple_debug),
143    hash: Some(tuple_hash),
144    invariants: None,
145    parse: None,
146    parse_bytes: None,
147    try_from: None,
148    try_into_inner: None,
149    try_borrow_inner: None,
150    partial_eq: Some(tuple_partial_eq),
151    partial_cmp: Some(tuple_partial_cmp),
152    cmp: Some(tuple_cmp),
153};
154
155// Type operations for all tuples
156static TUPLE_TYPE_OPS: TypeOpsIndirect = TypeOpsIndirect {
157    drop_in_place: tuple_drop,
158    default_in_place: None,
159    clone_into: None,
160    is_truthy: None,
161};
162
163/// Type-erased type_name for tuples - reads field types from the shape
164fn tuple_type_name(
165    shape: &'static Shape,
166    f: &mut fmt::Formatter<'_>,
167    opts: TypeNameOpts,
168) -> fmt::Result {
169    let st = match &shape.ty {
170        Type::User(UserType::Struct(st)) if st.kind == StructKind::Tuple => st,
171        _ => return write!(f, "(?)"),
172    };
173
174    write!(f, "(")?;
175    // Use for_children() for the element types since they're nested inside the tuple
176    let child_opts = opts.for_children();
177    for (i, field) in st.fields.iter().enumerate() {
178        if i > 0 {
179            write!(f, ", ")?;
180        }
181        if let Some(opts) = child_opts {
182            field.shape.get().write_type_name(f, opts)?;
183        } else {
184            write!(f, "…")?;
185        }
186    }
187    // Trailing comma for single-element tuples
188    if st.fields.len() == 1 {
189        write!(f, ",")?;
190    }
191    write!(f, ")")?;
192    Ok(())
193}
194
195macro_rules! impl_facet_for_tuple {
196    // Used to implement the next bigger tuple type, by taking the next typename & associated index
197    // out of `remaining`, if it exists.
198    {
199        continue from ($($elems:ident.$idx:tt,)+),
200        remaining ()
201    } => {};
202    {
203        continue from ($($elems:ident.$idx:tt,)+),
204        remaining ($next:ident.$nextidx:tt, $($remaining:ident.$remainingidx:tt,)*)
205    } => {
206        impl_facet_for_tuple! {
207            impl ($($elems.$idx,)+ $next.$nextidx,),
208            remaining ($($remaining.$remainingidx,)*)
209        }
210    };
211    // Actually generate the trait implementation, and keep the remaining possible elements around
212    {
213        impl ($($elems:ident.$idx:tt,)+),
214        remaining ($($remaining:ident.$remainingidx:tt,)*)
215    } => {
216        unsafe impl<'a $(, $elems)+> Facet<'a> for ($($elems,)+)
217        where
218            $($elems: Facet<'a>,)+
219        {
220            const SHAPE: &'static Shape = &const {
221                ShapeBuilder::for_sized::<Self>(
222                    if 1 == [$($elems::SHAPE),+].len() {
223                        "(_,)"
224                    } else {
225                        "(…)"
226                    }
227                )
228                .type_name(tuple_type_name)
229                .ty(Type::User(UserType::Struct(StructType {
230                    repr: Repr::default(),
231                    kind: StructKind::Tuple,
232                    fields: &const {[
233                        $(FieldBuilder::new(stringify!($idx), crate::shape_of::<$elems>, mem::offset_of!(Self, $idx)).build(),)+
234                    ]}
235                })))
236                .def(Def::Undefined)
237                .vtable_indirect(&TUPLE_VTABLE)
238                .type_ops_indirect(&TUPLE_TYPE_OPS)
239                .build()
240            };
241        }
242
243        impl_facet_for_tuple! {
244            continue from ($($elems.$idx,)+),
245            remaining ($($remaining.$remainingidx,)*)
246        }
247    };
248    // The entry point into this macro, all smaller tuple types get implemented as well.
249    { ($first:ident.$firstidx:tt $(, $remaining:ident.$remainingidx:tt)* $(,)?) } => {
250        impl_facet_for_tuple! {
251            impl ($first.$firstidx,),
252            remaining ($($remaining.$remainingidx,)*)
253        }
254    };
255}
256
257#[cfg(feature = "tuples-12")]
258impl_facet_for_tuple! {
259    (T0.0, T1.1, T2.2, T3.3, T4.4, T5.5, T6.6, T7.7, T8.8, T9.9, T10.10, T11.11)
260}
261
262#[cfg(not(feature = "tuples-12"))]
263impl_facet_for_tuple! {
264    (T0.0, T1.1, T2.2, T3.3)
265}