toasty_core/stmt/
num.rs

1//! Numeric type support for [`Value`], [`Expr`], and [`Type`].
2//!
3//! This module uses the `impl_num!` macro to generate, for each integer type
4//! (`i8`..`u64`):
5//!
6//! - `Type::is_{ty}()` -- type predicate
7//! - `Value::to_{ty}()` / `Value::to_{ty}_unwrap()` -- cross-width conversion
8//! - `From<{ty}> for Value` / `TryFrom<Value> for {ty}`
9//! - `PartialEq<{ty}>` for both `Value` and `Expr`
10//!
11//! # Examples
12//!
13//! ```
14//! use toasty_core::stmt::{Value, Type};
15//!
16//! let v = Value::from(42_i64);
17//! assert_eq!(v, 42_i64);
18//! assert_eq!(v.to_i64(), Some(42));
19//! assert!(Type::I64.is_i64());
20//! ```
21
22use super::{Expr, Type, Value};
23
24macro_rules! try_from {
25    ($v:expr, $ty:ty) => {
26        match $v {
27            Value::I8(v) => <$ty>::try_from(v).ok(),
28            Value::I16(v) => <$ty>::try_from(v).ok(),
29            Value::I32(v) => <$ty>::try_from(v).ok(),
30            Value::I64(v) => <$ty>::try_from(v).ok(),
31            Value::U8(v) => <$ty>::try_from(v).ok(),
32            Value::U16(v) => <$ty>::try_from(v).ok(),
33            Value::U32(v) => <$ty>::try_from(v).ok(),
34            Value::U64(v) => <$ty>::try_from(v).ok(),
35            _ => None,
36        }
37    };
38}
39
40macro_rules! impl_num {
41    (
42        $(
43            $variant:ident($ty:ty) {
44                $to:ident
45                $to_unwrap:ident
46                $is:ident
47            } )*
48    ) => {
49        impl Type {
50            $(
51                /// Returns `true` if this type matches the corresponding integer variant.
52                pub fn $is(&self) -> bool {
53                    matches!(self, Self::$variant)
54                }
55            )*
56        }
57
58        impl Value {
59            $(
60                /// Attempts to convert this value to the target integer type.
61                ///
62                /// Returns `None` if the value is not an integer variant or is out of
63                /// range for the target type. Conversion works across all integer
64                /// widths and signedness.
65                pub fn $to(&self) -> Option<$ty> {
66                    try_from!(*self, $ty)
67                }
68
69                /// Converts this value to the target integer type, panicking on failure.
70                ///
71                /// # Panics
72                ///
73                /// Panics if the value is not an integer variant or is out of range.
74                #[track_caller]
75                pub fn $to_unwrap(&self) -> $ty {
76                    try_from!(*self, $ty).expect("out of range integral type conversion attempted")
77                }
78            )*
79        }
80
81        $(
82            impl PartialEq<$ty> for Value {
83                fn eq(&self, other: &$ty) -> bool {
84                    try_from!(*self, $ty).map(|v| v == *other).unwrap_or(false)
85                }
86            }
87
88            impl PartialEq<Value> for $ty {
89                fn eq(&self, other: &Value) -> bool {
90                    other.eq(self)
91                }
92            }
93
94            impl PartialEq<$ty> for Expr {
95                fn eq(&self, other: &$ty) -> bool {
96                    match self {
97                        Expr::Value(value) => value.eq(other),
98                        _ => false,
99                    }
100                }
101            }
102
103            impl PartialEq<Expr> for $ty {
104                fn eq(&self, other: &Expr) -> bool {
105                    other.eq(self)
106                }
107            }
108
109            impl From<$ty> for Value {
110                fn from(value: $ty) -> Self {
111                    Self::$variant(value)
112                }
113            }
114
115            impl From<&$ty> for Value {
116                fn from(value: &$ty) -> Self {
117                    Self::$variant(*value)
118                }
119            }
120
121            impl TryFrom<Value> for $ty {
122                type Error = crate::Error;
123
124                fn try_from(value: Value) -> crate::Result<Self> {
125                    value.$to().ok_or_else(|| {
126                        crate::Error::type_conversion(value.clone(), stringify!($ty))
127                    })
128                }
129            }
130
131            #[cfg(feature = "assert-struct")]
132            impl assert_struct::Like<$ty> for Value {
133                fn like(&self, pattern: &$ty) -> bool {
134                    try_from!(*self, $ty).map(|v| v == *pattern).unwrap_or(false)
135                }
136            }
137
138            #[cfg(feature = "assert-struct")]
139            impl assert_struct::Like<$ty> for Expr {
140                fn like(&self, pattern: &$ty) -> bool {
141                    match self {
142                        Expr::Value(value) => value.like(pattern),
143                        _ => false,
144                    }
145                }
146            }
147        )*
148    };
149}
150
151impl_num! {
152    I8(i8) {
153        to_i8
154        to_i8_unwrap
155        is_i8
156    }
157    I16(i16) {
158        to_i16
159        to_i16_unwrap
160        is_i16
161    }
162    I32(i32) {
163        to_i32
164        to_i32_unwrap
165        is_i32
166    }
167    I64(i64) {
168        to_i64
169        to_i64_unwrap
170        is_i64
171    }
172    U8(u8) {
173        to_u8
174        to_u8_unwrap
175        is_u8
176    }
177    U16(u16) {
178        to_u16
179        to_u16_unwrap
180        is_u16
181    }
182    U32(u32) {
183        to_u32
184        to_u32_unwrap
185        is_u32
186    }
187    U64(u64) {
188        to_u64
189        to_u64_unwrap
190        is_u64
191    }
192}
193
194impl From<usize> for Value {
195    fn from(value: usize) -> Self {
196        Value::U64(value as u64)
197    }
198}
199
200impl From<&usize> for Value {
201    fn from(value: &usize) -> Self {
202        Value::U64(*value as u64)
203    }
204}
205
206impl From<isize> for Value {
207    fn from(value: isize) -> Self {
208        Value::I64(value as i64)
209    }
210}
211
212impl From<&isize> for Value {
213    fn from(value: &isize) -> Self {
214        Value::I64(*value as i64)
215    }
216}
217
218#[cfg(feature = "assert-struct")]
219impl assert_struct::Like<usize> for Value {
220    fn like(&self, pattern: &usize) -> bool {
221        usize::try_from(self)
222            .map(|v| v == *pattern)
223            .unwrap_or(false)
224    }
225}
226
227#[cfg(feature = "assert-struct")]
228impl assert_struct::Like<usize> for Expr {
229    fn like(&self, pattern: &usize) -> bool {
230        match self {
231            Expr::Value(v) => v.like(pattern),
232            _ => false,
233        }
234    }
235}
236
237#[cfg(feature = "assert-struct")]
238impl assert_struct::Like<isize> for Value {
239    fn like(&self, pattern: &isize) -> bool {
240        isize::try_from(self)
241            .map(|v| v == *pattern)
242            .unwrap_or(false)
243    }
244}
245
246#[cfg(feature = "assert-struct")]
247impl assert_struct::Like<isize> for Expr {
248    fn like(&self, pattern: &isize) -> bool {
249        match self {
250            Expr::Value(v) => v.like(pattern),
251            _ => false,
252        }
253    }
254}
255
256// Pointer-sized integers convert from their fixed-size equivalents
257impl TryFrom<Value> for usize {
258    type Error = crate::Error;
259
260    fn try_from(value: Value) -> crate::Result<Self> {
261        (&value).try_into()
262    }
263}
264
265impl TryFrom<&Value> for usize {
266    type Error = crate::Error;
267
268    fn try_from(value: &Value) -> crate::Result<Self> {
269        let u64_val = value
270            .to_u64()
271            .ok_or_else(|| crate::Error::type_conversion(value.clone(), "usize"))?;
272        u64_val
273            .try_into()
274            .map_err(|_| crate::Error::type_conversion(Value::U64(u64_val), "usize"))
275    }
276}
277
278impl TryFrom<Value> for isize {
279    type Error = crate::Error;
280
281    fn try_from(value: Value) -> crate::Result<Self> {
282        (&value).try_into()
283    }
284}
285
286impl TryFrom<&Value> for isize {
287    type Error = crate::Error;
288
289    fn try_from(value: &Value) -> crate::Result<Self> {
290        let i64_val = value
291            .to_i64()
292            .ok_or_else(|| crate::Error::type_conversion(value.clone(), "isize"))?;
293        i64_val
294            .try_into()
295            .map_err(|_| crate::Error::type_conversion(Value::I64(i64_val), "isize"))
296    }
297}