Skip to main content

dinoco_engine/
value.rs

1use chrono::{DateTime, NaiveDate, Utc};
2use serde::{Deserialize, Serialize};
3use std::convert::TryFrom;
4
5use crate::DinocoError;
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
8pub enum DinocoValue {
9    Null,
10    Integer(i64),
11    Float(f64),
12    String(String),
13    Enum(String, String),
14    Boolean(bool),
15
16    Bytes(Vec<u8>),
17
18    Json(serde_json::Value),
19    DateTime(DateTime<Utc>),
20    Date(NaiveDate),
21}
22
23impl From<&str> for DinocoValue {
24    fn from(value: &str) -> Self {
25        DinocoValue::String(value.to_string())
26    }
27}
28
29impl From<String> for DinocoValue {
30    fn from(value: String) -> Self {
31        DinocoValue::String(value.to_string())
32    }
33}
34
35impl From<i64> for DinocoValue {
36    fn from(value: i64) -> Self {
37        DinocoValue::Integer(value)
38    }
39}
40
41impl From<f64> for DinocoValue {
42    fn from(value: f64) -> Self {
43        DinocoValue::Float(value)
44    }
45}
46
47impl From<bool> for DinocoValue {
48    fn from(value: bool) -> Self {
49        DinocoValue::Boolean(value)
50    }
51}
52
53impl From<Vec<u8>> for DinocoValue {
54    fn from(value: Vec<u8>) -> Self {
55        DinocoValue::Bytes(value)
56    }
57}
58
59impl From<DateTime<Utc>> for DinocoValue {
60    fn from(value: DateTime<Utc>) -> Self {
61        DinocoValue::DateTime(value)
62    }
63}
64
65impl From<NaiveDate> for DinocoValue {
66    fn from(value: NaiveDate) -> Self {
67        DinocoValue::Date(value)
68    }
69}
70
71impl TryFrom<DinocoValue> for String {
72    type Error = DinocoError;
73
74    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
75        match value {
76            DinocoValue::String(s) => Ok(s),
77            DinocoValue::Enum(_, s) => Ok(s),
78            DinocoValue::Bytes(b) => String::from_utf8(b).map_err(|_| DinocoError::ParseError("Invalid UTF-8".into())),
79            DinocoValue::DateTime(dt) => Ok(dt.to_rfc3339()),
80            DinocoValue::Date(date) => Ok(date.to_string()),
81            _ => Err(DinocoError::ParseError("Expected String".into())),
82        }
83    }
84}
85
86impl TryFrom<DinocoValue> for i64 {
87    type Error = DinocoError;
88
89    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
90        match value {
91            DinocoValue::Integer(i) => Ok(i),
92            DinocoValue::Boolean(b) => Ok(if b { 1 } else { 0 }),
93            _ => Err(DinocoError::ParseError("Expected i64".into())),
94        }
95    }
96}
97
98impl TryFrom<DinocoValue> for f64 {
99    type Error = DinocoError;
100
101    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
102        match value {
103            DinocoValue::Float(f) => Ok(f),
104            DinocoValue::Integer(i) => Ok(i as f64),
105            _ => Err(DinocoError::ParseError("Expected f64".into())),
106        }
107    }
108}
109
110impl TryFrom<DinocoValue> for bool {
111    type Error = DinocoError;
112
113    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
114        match value {
115            DinocoValue::Boolean(b) => Ok(b),
116            DinocoValue::Integer(i) => Ok(i != 0),
117            _ => Err(DinocoError::ParseError("Expected bool".into())),
118        }
119    }
120}
121
122impl TryFrom<DinocoValue> for Vec<u8> {
123    type Error = DinocoError;
124
125    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
126        match value {
127            DinocoValue::Bytes(b) => Ok(b),
128            DinocoValue::String(s) => Ok(s.into_bytes()),
129            DinocoValue::Enum(_, s) => Ok(s.into_bytes()),
130            _ => Err(DinocoError::ParseError("Expected bytes".into())),
131        }
132    }
133}
134
135impl TryFrom<DinocoValue> for DateTime<Utc> {
136    type Error = DinocoError;
137
138    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
139        match value {
140            DinocoValue::DateTime(dt) => Ok(dt),
141            DinocoValue::String(value) => parse_datetime_string(&value),
142            DinocoValue::Bytes(value) => {
143                let value =
144                    String::from_utf8(value).map_err(|_| DinocoError::ParseError("Invalid UTF-8 datetime".into()))?;
145
146                parse_datetime_string(&value)
147            }
148            _ => Err(DinocoError::ParseError("Expected DateTime<Utc>".into())),
149        }
150    }
151}
152
153impl TryFrom<DinocoValue> for NaiveDate {
154    type Error = DinocoError;
155
156    fn try_from(value: DinocoValue) -> Result<Self, Self::Error> {
157        match value {
158            DinocoValue::Date(date) => Ok(date),
159            DinocoValue::String(value) => parse_date_string(&value),
160            DinocoValue::Bytes(value) => {
161                let value =
162                    String::from_utf8(value).map_err(|_| DinocoError::ParseError("Invalid UTF-8 date".into()))?;
163
164                parse_date_string(&value)
165            }
166            _ => Err(DinocoError::ParseError("Expected NaiveDate".into())),
167        }
168    }
169}
170
171pub(crate) fn parse_datetime_string(value: &str) -> Result<DateTime<Utc>, DinocoError> {
172    if let Ok(datetime) = DateTime::parse_from_rfc3339(value) {
173        return Ok(datetime.with_timezone(&Utc));
174    }
175
176    if let Ok(datetime) = DateTime::parse_from_rfc2822(value) {
177        return Ok(datetime.with_timezone(&Utc));
178    }
179
180    let val = value.trim();
181    let val = val.strip_suffix(" UTC").unwrap_or(val);
182    let val = val.strip_suffix("Z").unwrap_or(val);
183
184    let formats = ["%Y-%m-%d %H:%M:%S%.f", "%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S%.f", "%Y-%m-%dT%H:%M:%S"];
185
186    for format in formats {
187        if let Ok(datetime) = chrono::NaiveDateTime::parse_from_str(val, format) {
188            return Ok(DateTime::<Utc>::from_naive_utc_and_offset(datetime, Utc));
189        }
190    }
191
192    Err(DinocoError::ParseError(format!("Expected DateTime<Utc>, got '{}'", value)))
193}
194
195pub(crate) fn parse_date_string(value: &str) -> Result<NaiveDate, DinocoError> {
196    NaiveDate::parse_from_str(value.trim(), "%Y-%m-%d")
197        .map_err(|_| DinocoError::ParseError(format!("Expected NaiveDate, got '{}'", value)))
198}