1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
use {
    super::ValueError::ValueToExprConversionFailure,
    crate::{
        ast::AstLiteral,
        ast::{DateTimeField, Expr},
        chrono::{DateTime, Utc},
        data::Interval,
        prelude::DataType,
        prelude::Value,
        result::Error,
        result::Result,
    },
    bigdecimal::{BigDecimal, FromPrimitive},
    serde_json::{Map as JsonMap, Value as JsonValue},
    uuid::Uuid,
};

impl TryFrom<Value> for Expr {
    type Error = Error;

    fn try_from(value: Value) -> Result<Self> {
        const SECOND: i64 = 1_000_000;

        let expr = match value {
            Value::Bool(v) => Expr::Literal(AstLiteral::Boolean(v)),
            Value::I8(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i8(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::I16(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i16(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::I32(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i32(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::I64(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i64(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::I128(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i128(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::U8(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_u8(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::F64(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_f64(v).ok_or(ValueToExprConversionFailure)?,
            )),
            Value::Decimal(v) => Expr::Literal(AstLiteral::Number(
                BigDecimal::from_f64(v.try_into().map_err(|_| ValueToExprConversionFailure)?)
                    .ok_or(ValueToExprConversionFailure)?,
            )),
            Value::Str(v) => Expr::Literal(AstLiteral::QuotedString(v)),
            Value::Bytea(v) => Expr::Literal(AstLiteral::HexString(hex::encode(v))),
            Value::Date(v) => Expr::TypedString {
                data_type: DataType::Date,
                value: v.to_string(),
            },
            Value::Timestamp(v) => Expr::TypedString {
                data_type: DataType::Timestamp,
                value: DateTime::<Utc>::from_utc(v, Utc).to_string(),
            },
            Value::Time(v) => Expr::TypedString {
                data_type: DataType::Time,
                value: v.to_string(),
            },
            Value::Interval(v) => match v {
                Interval::Month(v) => Expr::Interval {
                    expr: Box::new(Expr::Literal(AstLiteral::Number(
                        BigDecimal::from_i32(v).ok_or(ValueToExprConversionFailure)?,
                    ))),
                    leading_field: Some(DateTimeField::Month),
                    last_field: None,
                },
                Interval::Microsecond(v) => Expr::Interval {
                    expr: Box::new(Expr::Literal(AstLiteral::Number(
                        BigDecimal::from_i64(v / SECOND).ok_or(ValueToExprConversionFailure)?,
                    ))),
                    leading_field: Some(DateTimeField::Second),
                    last_field: None,
                },
            },
            Value::Uuid(v) => Expr::Literal(AstLiteral::QuotedString(
                Uuid::from_u128(v).hyphenated().to_string(),
            )),
            Value::Map(v) => {
                let json: JsonValue = v
                    .into_iter()
                    .map(|(key, value)| value.try_into().map(|value| (key, value)))
                    .collect::<Result<Vec<(String, JsonValue)>>>()
                    .map(|v| JsonMap::from_iter(v).into())
                    .map_err(|_| ValueToExprConversionFailure)?;

                Expr::Literal(AstLiteral::QuotedString(json.to_string()))
            }
            Value::List(v) => {
                let json: JsonValue = v
                    .into_iter()
                    .map(|value| value.try_into())
                    .collect::<Result<Vec<JsonValue>>>()
                    .map(|v| v.into())
                    .map_err(|_| ValueToExprConversionFailure)?;

                Expr::Literal(AstLiteral::QuotedString(json.to_string()))
            }
            Value::Null => Expr::Literal(AstLiteral::Null),
        };

        Ok(expr)
    }
}

#[cfg(test)]
mod tests {
    use {
        crate::{
            ast::{AstLiteral, DateTimeField, Expr},
            data::Interval,
            prelude::{DataType, Value},
        },
        bigdecimal::BigDecimal,
        bigdecimal::FromPrimitive,
        chrono::{NaiveDate, NaiveTime},
        rust_decimal::Decimal,
        std::collections::HashMap,
    };

    #[test]
    fn value_to_expr() {
        assert_eq!(
            Value::Bool(true).try_into(),
            Ok(Expr::Literal(AstLiteral::Boolean(true)))
        );

        assert_eq!(
            Value::I8(127).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i8(127).unwrap()
            )))
        );
        assert_eq!(
            Value::I16(32767).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i16(32767).unwrap()
            )))
        );
        assert_eq!(
            Value::I32(2147483647).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i32(2147483647).unwrap()
            )))
        );
        assert_eq!(
            Value::I64(64).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i64(64).unwrap()
            )))
        );
        assert_eq!(
            Value::I128(128).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_i128(128).unwrap()
            )))
        );
        assert_eq!(
            Value::U8(8).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_u8(8).unwrap()
            )))
        );
        assert_eq!(
            Value::F64(64.4).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_f64(64.4).unwrap()
            )))
        );
        assert_eq!(
            Value::Decimal(Decimal::new(315, 2)).try_into(),
            Ok(Expr::Literal(AstLiteral::Number(
                BigDecimal::from_f64(3.15).unwrap()
            )))
        );
        assert_eq!(
            Value::Str("data".to_owned()).try_into(),
            Ok(Expr::Literal(AstLiteral::QuotedString("data".to_owned())))
        );
        assert_eq!(
            Value::Bytea(hex::decode("1234").unwrap()).try_into(),
            Ok(Expr::Literal(AstLiteral::HexString("1234".to_owned())))
        );
        assert_eq!(
            Value::Date(NaiveDate::from_ymd(2022, 11, 3)).try_into(),
            Ok(Expr::TypedString {
                data_type: DataType::Date,
                value: "2022-11-03".to_owned(),
            })
        );
        assert_eq!(
            Value::Timestamp(NaiveDate::from_ymd(2022, 11, 3).and_hms_milli(8, 5, 30, 900))
                .try_into(),
            Ok(Expr::TypedString {
                data_type: DataType::Timestamp,
                value: "2022-11-03 08:05:30.900 UTC".to_owned(),
            }),
        );
        assert_eq!(
            Value::Time(NaiveTime::from_hms(20, 11, 59)).try_into(),
            Ok(Expr::TypedString {
                data_type: DataType::Time,
                value: "20:11:59".to_owned()
            }),
        );
        assert_eq!(
            Value::Interval(Interval::Month(1)).try_into(),
            Ok(Expr::Interval {
                expr: Box::new(Expr::Literal(AstLiteral::Number(
                    BigDecimal::from_i64(1).unwrap()
                ))),
                leading_field: Some(DateTimeField::Month),
                last_field: None
            })
        );
        assert_eq!(
            Value::Uuid(195965723427462096757863453463987888808).try_into(),
            Ok(Expr::Literal(AstLiteral::QuotedString(
                "936da01f-9abd-4d9d-80c7-02af85c822a8".to_owned()
            )))
        );
        assert_eq!(
            Value::Map(HashMap::from([("a".to_owned(), Value::Bool(true))])).try_into(),
            Ok(Expr::Literal(AstLiteral::QuotedString(
                "{\"a\":true}".to_owned()
            )))
        );
        assert_eq!(
            Value::List(vec![
                Value::I64(1),
                Value::Bool(true),
                Value::Str("a".to_owned())
            ])
            .try_into(),
            Ok(Expr::Literal(AstLiteral::QuotedString(
                "[1,true,\"a\"]".to_owned()
            )))
        );
        assert_eq!(Value::Null.try_into(), Ok(Expr::Literal(AstLiteral::Null)));
    }
}