1use std::collections::BTreeMap;
2use std::str::FromStr;
3
4use chrono::{DateTime, NaiveDate, Utc};
5pub use rust_decimal::Decimal;
6use rust_decimal::prelude::ToPrimitive;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum DataType {
10 Bool,
11 I64,
12 U64,
13 F64,
14 Decimal,
15 Text,
16 Json,
17 Date,
18 Timestamp,
19}
20
21#[derive(Debug, Clone, PartialEq)]
22pub enum Value {
23 Null,
24 Bool(bool),
25 I64(i64),
26 U64(u64),
27 F64(f64),
28 Decimal(Decimal),
29 Text(String),
30 Json(serde_json::Value),
31 Date(NaiveDate),
32 Timestamp(DateTime<Utc>),
33 Object(BTreeMap<String, Value>),
34 List(Vec<Value>),
35}
36
37impl From<&str> for Value {
38 fn from(value: &str) -> Self {
39 Self::Text(value.to_owned())
40 }
41}
42
43impl From<String> for Value {
44 fn from(value: String) -> Self {
45 Self::Text(value)
46 }
47}
48
49impl From<i64> for Value {
50 fn from(value: i64) -> Self {
51 Self::I64(value)
52 }
53}
54
55impl From<i32> for Value {
56 fn from(value: i32) -> Self {
57 Self::I64(i64::from(value))
58 }
59}
60
61impl From<i16> for Value {
62 fn from(value: i16) -> Self {
63 Self::I64(i64::from(value))
64 }
65}
66
67impl From<u64> for Value {
68 fn from(value: u64) -> Self {
69 Self::U64(value)
70 }
71}
72
73impl From<u32> for Value {
74 fn from(value: u32) -> Self {
75 Self::U64(u64::from(value))
76 }
77}
78
79impl From<u16> for Value {
80 fn from(value: u16) -> Self {
81 Self::U64(u64::from(value))
82 }
83}
84
85impl From<f64> for Value {
86 fn from(value: f64) -> Self {
87 Self::F64(value)
88 }
89}
90
91impl From<f32> for Value {
92 fn from(value: f32) -> Self {
93 Self::F64(f64::from(value))
94 }
95}
96
97impl From<bool> for Value {
98 fn from(value: bool) -> Self {
99 Self::Bool(value)
100 }
101}
102
103impl From<Decimal> for Value {
104 fn from(value: Decimal) -> Self {
105 Self::Decimal(value)
106 }
107}
108
109impl From<serde_json::Value> for Value {
110 fn from(value: serde_json::Value) -> Self {
111 Self::Json(value)
112 }
113}
114
115impl From<NaiveDate> for Value {
116 fn from(value: NaiveDate) -> Self {
117 Self::Date(value)
118 }
119}
120
121impl From<DateTime<Utc>> for Value {
122 fn from(value: DateTime<Utc>) -> Self {
123 Self::Timestamp(value)
124 }
125}
126
127impl Value {
128 pub fn object(record: crate::Record) -> Self {
129 Self::Object(record)
130 }
131
132 pub fn try_i64(&self) -> Option<i64> {
133 match self {
134 Self::I64(value) => Some(*value),
135 Self::U64(value) => i64::try_from(*value).ok(),
136 Self::Decimal(value) => value.to_i64(),
137 _ => None,
138 }
139 }
140
141 pub fn try_u64(&self) -> Option<u64> {
142 match self {
143 Self::U64(value) => Some(*value),
144 Self::I64(value) => u64::try_from(*value).ok(),
145 Self::Decimal(value) => value.to_u64(),
146 _ => None,
147 }
148 }
149
150 pub fn try_decimal(&self) -> Option<Decimal> {
151 match self {
152 Self::Decimal(value) => Some(*value),
153 Self::I64(value) => Some(Decimal::from(*value)),
154 Self::U64(value) => Some(Decimal::from(*value)),
155 Self::Text(value) => Decimal::from_str(value).ok(),
156 _ => None,
157 }
158 }
159
160 pub fn try_f64(&self) -> Option<f64> {
161 match self {
162 Self::F64(value) => Some(*value),
163 Self::I64(value) => Some(*value as f64),
164 Self::U64(value) => Some(*value as f64),
165 Self::Decimal(value) => value.to_f64(),
166 _ => None,
167 }
168 }
169
170 pub fn try_text(&self) -> Option<&str> {
171 match self {
172 Self::Text(value) => Some(value),
173 _ => None,
174 }
175 }
176
177 pub fn try_bool(&self) -> Option<bool> {
178 match self {
179 Self::Bool(value) => Some(*value),
180 _ => None,
181 }
182 }
183
184 pub fn try_date(&self) -> Option<NaiveDate> {
185 match self {
186 Self::Date(value) => Some(*value),
187 Self::Text(value) => {
188 if let Ok(nd) = chrono::NaiveDate::parse_from_str(value, "%Y-%m-%d") {
189 return Some(nd);
190 }
191 None
192 }
193 _ => None,
194 }
195 }
196
197 pub fn try_timestamp(&self) -> Option<DateTime<Utc>> {
198 match self {
199 Self::Timestamp(value) => Some(*value),
200 Self::Text(value) => {
201 if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(value) {
202 return Some(dt.with_timezone(&chrono::Utc));
203 }
204 if let Ok(ndt) = chrono::NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S") {
205 return Some(chrono::DateTime::from_naive_utc_and_offset(ndt, chrono::Utc));
206 }
207 if let Ok(nd) = chrono::NaiveDate::parse_from_str(value, "%Y-%m-%d") {
208 let ndt = nd.and_hms_opt(0, 0, 0)?;
209 return Some(chrono::DateTime::from_naive_utc_and_offset(ndt, chrono::Utc));
210 }
211 None
212 }
213 _ => None,
214 }
215 }
216
217 pub fn to_json_value(&self) -> serde_json::Value {
218 match self {
219 Self::Null => serde_json::Value::Null,
220 Self::Bool(value) => serde_json::Value::Bool(*value),
221 Self::I64(value) => serde_json::Value::from(*value),
222 Self::U64(value) => serde_json::Value::from(*value),
223 Self::F64(value) => serde_json::Number::from_f64(*value)
224 .map(serde_json::Value::Number)
225 .unwrap_or(serde_json::Value::Null),
226 Self::Decimal(value) => serde_json::Value::String(value.to_string()),
227 Self::Text(value) => serde_json::Value::String(value.clone()),
228 Self::Json(value) => value.clone(),
229 Self::Date(value) => serde_json::Value::String(value.to_string()),
230 Self::Timestamp(value) => serde_json::Value::String(value.to_rfc3339()),
231 Self::Object(record) => crate::record_to_json_value(record),
232 Self::List(values) => {
233 serde_json::Value::Array(values.iter().map(Value::to_json_value).collect())
234 }
235 }
236 }
237}