gluesql_core/ast_builder/expr/
numeric.rs1use {
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}