gluesql_core/data/value/
expr.rs

1use {
2    super::ValueError::ValueToExprConversionFailure,
3    crate::{
4        ast::{AstLiteral, DateTimeField, Expr},
5        chrono::{TimeZone, Utc},
6        data::Interval,
7        prelude::{DataType, Value},
8        result::{Error, Result},
9    },
10    bigdecimal::{BigDecimal, FromPrimitive},
11    serde_json::{Map as JsonMap, Value as JsonValue},
12    uuid::Uuid,
13};
14
15impl TryFrom<Value> for Expr {
16    type Error = Error;
17
18    fn try_from(value: Value) -> Result<Self> {
19        const SECOND: i64 = 1_000_000;
20
21        let expr = match value {
22            Value::Bool(v) => Expr::Literal(AstLiteral::Boolean(v)),
23            Value::I8(v) => Expr::Literal(AstLiteral::Number(
24                BigDecimal::from_i8(v).ok_or(ValueToExprConversionFailure)?,
25            )),
26            Value::I16(v) => Expr::Literal(AstLiteral::Number(
27                BigDecimal::from_i16(v).ok_or(ValueToExprConversionFailure)?,
28            )),
29            Value::I32(v) => Expr::Literal(AstLiteral::Number(
30                BigDecimal::from_i32(v).ok_or(ValueToExprConversionFailure)?,
31            )),
32            Value::I64(v) => Expr::Literal(AstLiteral::Number(
33                BigDecimal::from_i64(v).ok_or(ValueToExprConversionFailure)?,
34            )),
35            Value::I128(v) => Expr::Literal(AstLiteral::Number(
36                BigDecimal::from_i128(v).ok_or(ValueToExprConversionFailure)?,
37            )),
38            Value::U8(v) => Expr::Literal(AstLiteral::Number(
39                BigDecimal::from_u8(v).ok_or(ValueToExprConversionFailure)?,
40            )),
41            Value::U16(v) => Expr::Literal(AstLiteral::Number(
42                BigDecimal::from_u16(v).ok_or(ValueToExprConversionFailure)?,
43            )),
44            Value::U32(v) => Expr::Literal(AstLiteral::Number(
45                BigDecimal::from_u32(v).ok_or(ValueToExprConversionFailure)?,
46            )),
47            Value::U64(v) => Expr::Literal(AstLiteral::Number(
48                BigDecimal::from_u64(v).ok_or(ValueToExprConversionFailure)?,
49            )),
50            Value::U128(v) => Expr::Literal(AstLiteral::Number(
51                BigDecimal::from_u128(v).ok_or(ValueToExprConversionFailure)?,
52            )),
53            Value::F32(v) => Expr::Literal(AstLiteral::Number(
54                BigDecimal::from_f32(v).ok_or(ValueToExprConversionFailure)?,
55            )),
56            Value::F64(v) => Expr::Literal(AstLiteral::Number(
57                BigDecimal::from_f64(v).ok_or(ValueToExprConversionFailure)?,
58            )),
59            Value::Decimal(v) => Expr::Literal(AstLiteral::Number(
60                BigDecimal::from_f64(v.try_into().map_err(|_| ValueToExprConversionFailure)?)
61                    .ok_or(ValueToExprConversionFailure)?,
62            )),
63            Value::Str(v) => Expr::Literal(AstLiteral::QuotedString(v)),
64            Value::Bytea(v) => Expr::Literal(AstLiteral::HexString(hex::encode(v))),
65            Value::Inet(v) => Expr::Literal(AstLiteral::QuotedString(v.to_string())),
66            Value::Date(v) => Expr::TypedString {
67                data_type: DataType::Date,
68                value: v.to_string(),
69            },
70            Value::Timestamp(v) => Expr::TypedString {
71                data_type: DataType::Timestamp,
72                value: Utc.from_utc_datetime(&v).to_string(),
73            },
74            Value::Time(v) => Expr::TypedString {
75                data_type: DataType::Time,
76                value: v.to_string(),
77            },
78            Value::Interval(v) => match v {
79                Interval::Month(v) => Expr::Interval {
80                    expr: Box::new(Expr::Literal(AstLiteral::Number(
81                        BigDecimal::from_i32(v).ok_or(ValueToExprConversionFailure)?,
82                    ))),
83                    leading_field: Some(DateTimeField::Month),
84                    last_field: None,
85                },
86                Interval::Microsecond(v) => Expr::Interval {
87                    expr: Box::new(Expr::Literal(AstLiteral::Number(
88                        BigDecimal::from_i64(v / SECOND).ok_or(ValueToExprConversionFailure)?,
89                    ))),
90                    leading_field: Some(DateTimeField::Second),
91                    last_field: None,
92                },
93            },
94            Value::Uuid(v) => Expr::Literal(AstLiteral::QuotedString(
95                Uuid::from_u128(v).hyphenated().to_string(),
96            )),
97            Value::Map(v) => {
98                let json: JsonValue = v
99                    .into_iter()
100                    .map(|(key, value)| value.try_into().map(|value| (key, value)))
101                    .collect::<Result<Vec<(String, JsonValue)>>>()
102                    .map(|v| JsonMap::from_iter(v).into())
103                    .map_err(|_| ValueToExprConversionFailure)?;
104
105                Expr::Literal(AstLiteral::QuotedString(json.to_string()))
106            }
107            Value::List(v) => {
108                let json: JsonValue = v
109                    .into_iter()
110                    .map(|value| value.try_into())
111                    .collect::<Result<Vec<JsonValue>>>()
112                    .map(|v| v.into())
113                    .map_err(|_| ValueToExprConversionFailure)?;
114
115                Expr::Literal(AstLiteral::QuotedString(json.to_string()))
116            }
117            Value::Point(v) => Expr::Literal(AstLiteral::QuotedString(v.to_string())),
118            Value::Null => Expr::Literal(AstLiteral::Null),
119        };
120
121        Ok(expr)
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use {
128        crate::{
129            ast::{AstLiteral, DateTimeField, Expr},
130            data::{Interval, Point},
131            prelude::{DataType, Value},
132        },
133        bigdecimal::{BigDecimal, FromPrimitive},
134        chrono::{NaiveDate, NaiveTime},
135        rust_decimal::Decimal,
136        std::collections::BTreeMap,
137    };
138
139    #[test]
140    fn value_to_expr() {
141        assert_eq!(
142            Value::Bool(true).try_into(),
143            Ok(Expr::Literal(AstLiteral::Boolean(true)))
144        );
145
146        assert_eq!(
147            Value::I8(127).try_into(),
148            Ok(Expr::Literal(AstLiteral::Number(
149                BigDecimal::from_i8(127).unwrap()
150            )))
151        );
152        assert_eq!(
153            Value::I16(32767).try_into(),
154            Ok(Expr::Literal(AstLiteral::Number(
155                BigDecimal::from_i16(32767).unwrap()
156            )))
157        );
158        assert_eq!(
159            Value::I32(2147483647).try_into(),
160            Ok(Expr::Literal(AstLiteral::Number(
161                BigDecimal::from_i32(2147483647).unwrap()
162            )))
163        );
164        assert_eq!(
165            Value::I64(64).try_into(),
166            Ok(Expr::Literal(AstLiteral::Number(
167                BigDecimal::from_i64(64).unwrap()
168            )))
169        );
170        assert_eq!(
171            Value::I128(128).try_into(),
172            Ok(Expr::Literal(AstLiteral::Number(
173                BigDecimal::from_i128(128).unwrap()
174            )))
175        );
176        assert_eq!(
177            Value::U8(8).try_into(),
178            Ok(Expr::Literal(AstLiteral::Number(
179                BigDecimal::from_u8(8).unwrap()
180            )))
181        );
182        assert_eq!(
183            Value::U16(16).try_into(),
184            Ok(Expr::Literal(AstLiteral::Number(
185                BigDecimal::from_u16(16).unwrap()
186            )))
187        );
188        assert_eq!(
189            Value::U32(32).try_into(),
190            Ok(Expr::Literal(AstLiteral::Number(
191                BigDecimal::from_u32(32).unwrap()
192            )))
193        );
194        assert_eq!(
195            Value::U64(64).try_into(),
196            Ok(Expr::Literal(AstLiteral::Number(
197                BigDecimal::from_u64(64).unwrap()
198            )))
199        );
200        assert_eq!(
201            Value::U128(128).try_into(),
202            Ok(Expr::Literal(AstLiteral::Number(
203                BigDecimal::from_u128(128).unwrap()
204            )))
205        );
206
207        assert_eq!(
208            Value::F32(64.4_f32).try_into(),
209            Ok(Expr::Literal(AstLiteral::Number(
210                BigDecimal::from_f32(64.4).unwrap()
211            )))
212        );
213        assert_eq!(
214            Value::F64(64.4).try_into(),
215            Ok(Expr::Literal(AstLiteral::Number(
216                BigDecimal::from_f64(64.4).unwrap()
217            )))
218        );
219        assert_eq!(
220            Value::Decimal(Decimal::new(315, 2)).try_into(),
221            Ok(Expr::Literal(AstLiteral::Number(
222                BigDecimal::from_f64(3.15).unwrap()
223            )))
224        );
225        assert_eq!(
226            Value::Str("data".to_owned()).try_into(),
227            Ok(Expr::Literal(AstLiteral::QuotedString("data".to_owned())))
228        );
229        assert_eq!(
230            Value::Bytea(hex::decode("1234").unwrap()).try_into(),
231            Ok(Expr::Literal(AstLiteral::HexString("1234".to_owned())))
232        );
233        assert_eq!(
234            Value::Date(NaiveDate::from_ymd_opt(2022, 11, 3).unwrap()).try_into(),
235            Ok(Expr::TypedString {
236                data_type: DataType::Date,
237                value: "2022-11-03".to_owned(),
238            })
239        );
240        assert_eq!(
241            Value::Timestamp(
242                NaiveDate::from_ymd_opt(2022, 11, 3)
243                    .unwrap()
244                    .and_hms_milli_opt(8, 5, 30, 900)
245                    .unwrap()
246            )
247            .try_into(),
248            Ok(Expr::TypedString {
249                data_type: DataType::Timestamp,
250                value: "2022-11-03 08:05:30.900 UTC".to_owned(),
251            }),
252        );
253        assert_eq!(
254            Value::Time(NaiveTime::from_hms_opt(20, 11, 59).unwrap()).try_into(),
255            Ok(Expr::TypedString {
256                data_type: DataType::Time,
257                value: "20:11:59".to_owned()
258            }),
259        );
260        assert_eq!(
261            Value::Interval(Interval::Month(1)).try_into(),
262            Ok(Expr::Interval {
263                expr: Box::new(Expr::Literal(AstLiteral::Number(
264                    BigDecimal::from_i64(1).unwrap()
265                ))),
266                leading_field: Some(DateTimeField::Month),
267                last_field: None
268            })
269        );
270        assert_eq!(
271            Value::Uuid(195965723427462096757863453463987888808).try_into(),
272            Ok(Expr::Literal(AstLiteral::QuotedString(
273                "936da01f-9abd-4d9d-80c7-02af85c822a8".to_owned()
274            )))
275        );
276        assert_eq!(
277            Value::Map(BTreeMap::from([("a".to_owned(), Value::Bool(true))])).try_into(),
278            Ok(Expr::Literal(AstLiteral::QuotedString(
279                "{\"a\":true}".to_owned()
280            )))
281        );
282        assert_eq!(
283            Value::List(vec![
284                Value::I64(1),
285                Value::Bool(true),
286                Value::Str("a".to_owned())
287            ])
288            .try_into(),
289            Ok(Expr::Literal(AstLiteral::QuotedString(
290                "[1,true,\"a\"]".to_owned()
291            )))
292        );
293        assert_eq!(Value::Null.try_into(), Ok(Expr::Literal(AstLiteral::Null)));
294        assert_eq!(
295            Value::Point(Point::new(0.31413, 0.3415)).try_into(),
296            Ok(Expr::Literal(AstLiteral::QuotedString(
297                "POINT(0.31413 0.3415)".to_owned()
298            )))
299        );
300    }
301}