facet_core/impls/core/
scalar.rs

1//! Scalar type implementations: bool, char, integers, floats
2//!
3//! Note: ConstTypeId, TypeId are in typeid.rs
4//! Note: (), PhantomData are in tuple_empty.rs and phantom.rs
5//! Note: str, char are in char_str.rs
6
7extern crate alloc;
8
9use crate::{
10    Def, Facet, NumericType, PrimitiveType, PtrConst, Shape, ShapeBuilder, Type, TypeOpsDirect,
11    VTableDirect, type_ops_direct, vtable_direct,
12};
13
14/// Generate a try_from function for an integer type that converts from any other integer.
15macro_rules! integer_try_from {
16    ($target:ty) => {{
17        /// # Safety
18        /// `dst` must be valid for writes, `src` must point to valid data of type described by `src_shape`
19        unsafe fn try_from_any(
20            dst: *mut $target,
21            src_shape: &'static Shape,
22            src: PtrConst,
23        ) -> Result<(), alloc::string::String> {
24            use core::convert::TryInto;
25
26            // Helper macro to handle the conversion with proper error handling
27            macro_rules! convert {
28                ($src_ty:ty) => {{
29                    let src_val = unsafe { *(src.as_byte_ptr() as *const $src_ty) };
30                    <$src_ty as TryInto<$target>>::try_into(src_val).map_err(|_| {
31                        alloc::format!(
32                            "conversion from {} to {} failed: value {} out of range",
33                            src_shape.type_identifier,
34                            stringify!($target),
35                            src_val
36                        )
37                    })
38                }};
39            }
40
41            // Match on the source shape to determine its type
42            let result: Result<$target, alloc::string::String> = match src_shape.type_identifier {
43                "i8" => convert!(i8),
44                "i16" => convert!(i16),
45                "i32" => convert!(i32),
46                "i64" => convert!(i64),
47                "i128" => convert!(i128),
48                "isize" => convert!(isize),
49                "u8" => convert!(u8),
50                "u16" => convert!(u16),
51                "u32" => convert!(u32),
52                "u64" => convert!(u64),
53                "u128" => convert!(u128),
54                "usize" => convert!(usize),
55                _ => Err(alloc::format!(
56                    "cannot convert {} to {}",
57                    src_shape.type_identifier,
58                    stringify!($target)
59                )),
60            };
61
62            match result {
63                Ok(value) => {
64                    unsafe { dst.write(value) };
65                    Ok(())
66                }
67                Err(e) => Err(e),
68            }
69        }
70        try_from_any
71    }};
72}
73
74// Truthiness helpers + TypeOps lifted out of const blocks - shared statics
75
76#[inline(always)]
77unsafe fn bool_truthy(value: PtrConst) -> bool {
78    *unsafe { value.get::<bool>() }
79}
80
81macro_rules! define_int_type_ops {
82    ($const_name:ident, $ty:ty, $fn_name:ident) => {
83        #[inline(always)]
84        unsafe fn $fn_name(value: PtrConst) -> bool {
85            *unsafe { value.get::<$ty>() } != 0
86        }
87
88        static $const_name: TypeOpsDirect = TypeOpsDirect {
89            is_truthy: Some($fn_name),
90    ..type_ops_direct!($ty => Default, Clone)
91        };
92    };
93}
94
95macro_rules! define_float_type_ops {
96    ($const_name:ident, $ty:ty, $fn_name:ident) => {
97        #[inline(always)]
98        unsafe fn $fn_name(value: PtrConst) -> bool {
99            let v = *unsafe { value.get::<$ty>() };
100            v != 0.0 && !v.is_nan()
101        }
102
103        static $const_name: TypeOpsDirect = TypeOpsDirect {
104            is_truthy: Some($fn_name),
105    ..type_ops_direct!($ty => Default, Clone)
106        };
107    };
108}
109
110static BOOL_TYPE_OPS: TypeOpsDirect = TypeOpsDirect {
111    is_truthy: Some(bool_truthy),
112    ..type_ops_direct!(bool => Default, Clone)
113};
114
115define_int_type_ops!(U8_TYPE_OPS, u8, u8_truthy);
116define_int_type_ops!(I8_TYPE_OPS, i8, i8_truthy);
117define_int_type_ops!(U16_TYPE_OPS, u16, u16_truthy);
118define_int_type_ops!(I16_TYPE_OPS, i16, i16_truthy);
119define_int_type_ops!(U32_TYPE_OPS, u32, u32_truthy);
120define_int_type_ops!(I32_TYPE_OPS, i32, i32_truthy);
121define_int_type_ops!(U64_TYPE_OPS, u64, u64_truthy);
122define_int_type_ops!(I64_TYPE_OPS, i64, i64_truthy);
123define_int_type_ops!(U128_TYPE_OPS, u128, u128_truthy);
124define_int_type_ops!(I128_TYPE_OPS, i128, i128_truthy);
125define_int_type_ops!(USIZE_TYPE_OPS, usize, usize_truthy);
126define_int_type_ops!(ISIZE_TYPE_OPS, isize, isize_truthy);
127
128define_float_type_ops!(F32_TYPE_OPS, f32, f32_truthy);
129define_float_type_ops!(F64_TYPE_OPS, f64, f64_truthy);
130
131unsafe impl Facet<'_> for bool {
132    const SHAPE: &'static Shape = &const {
133        const VTABLE: VTableDirect = vtable_direct!(bool =>
134            FromStr,
135            Display,
136            Debug,
137            Hash,
138            PartialEq,
139            PartialOrd,
140            Ord,
141        );
142
143        ShapeBuilder::for_sized::<bool>("bool")
144            .ty(Type::Primitive(PrimitiveType::Boolean))
145            .def(Def::Scalar)
146            .vtable_direct(&VTABLE)
147            .type_ops_direct(&BOOL_TYPE_OPS)
148            .eq()
149            .copy()
150            .send()
151            .sync()
152            .build()
153    };
154}
155
156macro_rules! impl_facet_for_integer {
157    ($type:ty, $type_ops:expr) => {
158        unsafe impl<'a> Facet<'a> for $type {
159            const SHAPE: &'static Shape = &const {
160                const VTABLE: VTableDirect = vtable_direct!($type =>
161                    FromStr,
162                    Display,
163                    Debug,
164                    Hash,
165                    PartialEq,
166                    PartialOrd,
167                    Ord,
168                    [try_from = integer_try_from!($type)],
169                );
170
171                ShapeBuilder::for_sized::<$type>(stringify!($type))
172                    .ty(Type::Primitive(PrimitiveType::Numeric(
173                        NumericType::Integer {
174                            signed: (1 as $type).checked_neg().is_some(),
175                        },
176                    )))
177                    .def(Def::Scalar)
178                    .vtable_direct(&VTABLE)
179                    .type_ops_direct($type_ops)
180                    .eq()
181                    .copy()
182                    .send()
183                    .sync()
184                    .build()
185            };
186        }
187    };
188}
189
190impl_facet_for_integer!(u8, &U8_TYPE_OPS);
191impl_facet_for_integer!(i8, &I8_TYPE_OPS);
192impl_facet_for_integer!(u16, &U16_TYPE_OPS);
193impl_facet_for_integer!(i16, &I16_TYPE_OPS);
194impl_facet_for_integer!(u32, &U32_TYPE_OPS);
195impl_facet_for_integer!(i32, &I32_TYPE_OPS);
196impl_facet_for_integer!(u64, &U64_TYPE_OPS);
197impl_facet_for_integer!(i64, &I64_TYPE_OPS);
198impl_facet_for_integer!(u128, &U128_TYPE_OPS);
199impl_facet_for_integer!(i128, &I128_TYPE_OPS);
200impl_facet_for_integer!(usize, &USIZE_TYPE_OPS);
201impl_facet_for_integer!(isize, &ISIZE_TYPE_OPS);
202
203unsafe impl Facet<'_> for f32 {
204    const SHAPE: &'static Shape = &const {
205        // f32 implements Debug, Display, Clone, Copy, Default, PartialEq, PartialOrd
206        // but NOT Eq, Ord, or Hash (because of NaN)
207        const VTABLE: VTableDirect = vtable_direct!(f32 =>
208            FromStr,
209            Display,
210            Debug,
211            PartialEq,
212            PartialOrd,
213        );
214
215        ShapeBuilder::for_sized::<f32>("f32")
216            .ty(Type::Primitive(PrimitiveType::Numeric(NumericType::Float)))
217            .def(Def::Scalar)
218            .vtable_direct(&VTABLE)
219            .type_ops_direct(&F32_TYPE_OPS)
220            .copy()
221            .send()
222            .sync()
223            .build()
224    };
225}
226
227unsafe impl Facet<'_> for f64 {
228    const SHAPE: &'static Shape = &const {
229        // f64 implements Debug, Display, Clone, Copy, Default, PartialEq, PartialOrd
230        // but NOT Eq, Ord, or Hash (because of NaN)
231        const VTABLE: VTableDirect = vtable_direct!(f64 =>
232            FromStr,
233            Display,
234            Debug,
235            PartialEq,
236            PartialOrd,
237        );
238
239        ShapeBuilder::for_sized::<f64>("f64")
240            .ty(Type::Primitive(PrimitiveType::Numeric(NumericType::Float)))
241            .def(Def::Scalar)
242            .vtable_direct(&VTABLE)
243            .type_ops_direct(&F64_TYPE_OPS)
244            .copy()
245            .send()
246            .sync()
247            .build()
248    };
249}
250
251#[cfg(test)]
252mod tests {
253    use crate::{Facet, TypeOps};
254
255    #[test]
256    fn test_scalar_shapes() {
257        assert!(bool::SHAPE.vtable.has_debug());
258        assert!(bool::SHAPE.vtable.has_display());
259        // Default is now in type_ops
260        assert!(
261            matches!(bool::SHAPE.type_ops, Some(TypeOps::Direct(ops)) if ops.default_in_place.is_some())
262        );
263
264        assert!(u32::SHAPE.vtable.has_debug());
265        assert!(u32::SHAPE.vtable.has_display());
266        assert!(u32::SHAPE.vtable.has_hash());
267
268        assert!(f64::SHAPE.vtable.has_debug());
269        assert!(f64::SHAPE.vtable.has_display());
270        assert!(!f64::SHAPE.vtable.has_hash()); // floats don't have Hash
271    }
272}