gluesql_core/ast_builder/expr/
numeric.rs

1use {
2    crate::{
3        ast::AstLiteral,
4        ast_builder::AstBuilderError,
5        result::{Error, Result},
6    },
7    bigdecimal::BigDecimal,
8    std::{borrow::Cow, str::FromStr},
9};
10
11#[derive(Clone, Debug)]
12pub enum NumericNode<'a> {
13    I8(i8),
14    I16(i16),
15    I32(i32),
16    I64(i64),
17    U8(u8),
18    U16(u16),
19    U32(u32),
20    U64(u64),
21    F32(f32),
22    F64(f64),
23    Str(Cow<'a, str>),
24}
25
26macro_rules! impl_from {
27    ($type: path, $name: ident) => {
28        impl<'a> From<$type> for NumericNode<'a> {
29            fn from(v: $type) -> Self {
30                NumericNode::$name(v)
31            }
32        }
33    };
34}
35
36impl_from!(i8, I8);
37impl_from!(i16, I16);
38impl_from!(i32, I32);
39impl_from!(i64, I64);
40impl_from!(u8, U8);
41impl_from!(u16, U16);
42impl_from!(u32, U32);
43impl_from!(u64, U64);
44impl_from!(f32, F32);
45impl_from!(f64, F64);
46
47impl<'a> From<String> for NumericNode<'a> {
48    fn from(v: String) -> Self {
49        Self::Str(Cow::Owned(v))
50    }
51}
52
53impl<'a> From<&'a str> for NumericNode<'a> {
54    fn from(v: &'a str) -> Self {
55        Self::Str(Cow::Borrowed(v))
56    }
57}
58
59impl<'a> TryFrom<NumericNode<'a>> for AstLiteral {
60    type Error = Error;
61
62    fn try_from(node: NumericNode<'a>) -> Result<Self> {
63        match node {
64            NumericNode::I8(v) => Ok(AstLiteral::Number(v.into())),
65            NumericNode::I16(v) => Ok(AstLiteral::Number(v.into())),
66            NumericNode::I32(v) => Ok(AstLiteral::Number(v.into())),
67            NumericNode::I64(v) => Ok(AstLiteral::Number(v.into())),
68            NumericNode::U8(v) => Ok(AstLiteral::Number(v.into())),
69            NumericNode::U16(v) => Ok(AstLiteral::Number(v.into())),
70            NumericNode::U32(v) => Ok(AstLiteral::Number(v.into())),
71            NumericNode::U64(v) => Ok(AstLiteral::Number(v.into())),
72            NumericNode::F32(v) => BigDecimal::try_from(v)
73                .map_err(|_| AstBuilderError::FailedToParseNumeric(v.to_string()).into())
74                .map(AstLiteral::Number),
75            NumericNode::F64(v) => BigDecimal::try_from(v)
76                .map_err(|_| AstBuilderError::FailedToParseNumeric(v.to_string()).into())
77                .map(AstLiteral::Number),
78            NumericNode::Str(v) => BigDecimal::from_str(&v)
79                .map_err(|_| AstBuilderError::FailedToParseNumeric(v.into_owned()).into())
80                .map(AstLiteral::Number),
81        }
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use {
88        crate::{
89            ast::AstLiteral,
90            ast_builder::{AstBuilderError, NumericNode},
91        },
92        bigdecimal::BigDecimal,
93        std::str::FromStr,
94    };
95
96    #[test]
97    fn numeric() {
98        let num = |n| Ok(AstLiteral::Number(BigDecimal::from_str(n).unwrap()));
99
100        assert_eq!(NumericNode::from(1_i8).try_into(), num("1"));
101        assert_eq!(NumericNode::from(1_i16).try_into(), num("1"));
102        assert_eq!(NumericNode::from(1_i32).try_into(), num("1"));
103        assert_eq!(NumericNode::from(1_i64).try_into(), num("1"));
104        assert_eq!(NumericNode::from(1_u8).try_into(), num("1"));
105        assert_eq!(NumericNode::from(1_u16).try_into(), num("1"));
106        assert_eq!(NumericNode::from(1_u32).try_into(), num("1"));
107        assert_eq!(NumericNode::from(1_u64).try_into(), num("1"));
108        assert_eq!(NumericNode::from(1.50_f32).try_into(), num("1.50"));
109        assert_eq!(NumericNode::from(4.125_f64).try_into(), num("4.125"));
110        assert_eq!(NumericNode::from("123.456").try_into(), num("123.456"));
111        assert_eq!(NumericNode::from("1.6".to_owned()).try_into(), num("1.6"));
112
113        assert_eq!(
114            AstLiteral::try_from(NumericNode::from(f32::NAN)),
115            Err(AstBuilderError::FailedToParseNumeric(f32::NAN.to_string()).into()),
116        );
117        assert_eq!(
118            AstLiteral::try_from(NumericNode::from(f64::NAN)),
119            Err(AstBuilderError::FailedToParseNumeric(f64::NAN.to_string()).into()),
120        );
121        assert_eq!(
122            AstLiteral::try_from(NumericNode::from("not a number")),
123            Err(AstBuilderError::FailedToParseNumeric("not a number".to_owned()).into()),
124        );
125    }
126}