Skip to main content

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