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}