Skip to main content

teaql_core/
value.rs

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}