1use chrono::prelude::*;
2use std::cmp::PartialEq;
3use std::fmt;
4use std::mem;
5
6pub enum DataType<'a> {
7 String(&'a str),
8 Int(i64),
9 Float(f64),
10 Bool(bool),
11 DateTime(chrono::DateTime<Utc>),
12}
13
14impl fmt::Debug for DataType<'_> {
15 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
16 match self {
17 DataType::String(s) => write!(f, "String({})", s),
18 DataType::Int(i) => write!(f, "Int({})", i),
19 DataType::Float(fl) => write!(f, "Float({})", fl),
20 DataType::Bool(b) => write!(f, "Bool({})", b),
21 DataType::DateTime(d) => write!(f, "DateTime({})", d),
22 }
23 }
24}
25
26impl PartialEq for DataType<'_> {
27 fn eq(&self, other: &Self) -> bool {
28 mem::discriminant(self) == mem::discriminant(other)
29 }
30}
31
32pub fn check_type(s: &str) -> DataType {
33 if let Some(b) = try_get_bool(s) {
34 DataType::Bool(b)
35 } else if let Some(f) = try_get_f64(s) {
36 if f.fract() == 0.0 {
37 if s.len() == 8 {
38 if let Some(d) = try_get_datetime(s) {
39 DataType::DateTime(d)
40 } else {
41 DataType::Int(f as i64)
42 }
43 } else {
44 DataType::Int(f as i64)
45 }
46 } else {
47 DataType::Float(f)
48 }
49 } else if let Some(d) = try_get_datetime(s) {
50 DataType::DateTime(d)
51 } else {
52 DataType::String(s)
53 }
54}
55
56fn try_get_bool(s: &str) -> Option<bool> {
57 match s.to_lowercase().as_str() {
58 "true" => Some(true),
59 "false" => Some(false),
60 _ => None,
61 }
62}
63
64fn try_get_f64(s: &str) -> Option<f64> {
65 if let Ok(f) = s.parse::<f64>() {
66 Some(f)
67 } else {
68 None
69 }
70}
71
72fn try_get_i64(s: &str) -> Option<i64> {
73 if let Ok(f) = s.parse::<i64>() {
74 Some(f)
75 } else {
76 None
77 }
78}
79
80fn try_get_datetime(s: &str) -> Option<DateTime<Utc>> {
81 if let Some(_) = try_get_i64(s) {
83 match s.len() {
85 8 => {
87 let y = &s[0..4].parse::<u32>().unwrap();
88 let m = &s[4..6].parse::<u32>().unwrap();
89 let d = &s[6..8].parse::<u32>().unwrap();
90 if is_date(*y, *m, *d) {
91 if let Ok(dt) = Utc.datetime_from_str(
92 format!("{}-{}-{} 00:00:00", &s[0..4], &s[4..6], &s[6..8]).as_str(),
93 "%Y-%m-%d %H:%M:%S",
94 ) {
95 Some(dt)
96 } else {
97 None
98 }
99 } else {
100 None
101 }
102 }
103 _ => None,
104 }
105 } else {
106 println!("{:?}", s.parse::<DateTime<Utc>>());
107 if let Ok(d) = s.parse::<DateTime<Utc>>() {
108 Some(d)
109 } else {
110 None
111 }
112 }
113}
114
115fn is_date(year: u32, month: u32, day: u32) -> bool {
116 if month < 1 || month > 12 {
117 false
118 } else {
119 match month {
120 1 | 3 | 5 | 7 | 8 | 10 | 12 if day > 0 && day < 32 => true,
121 4 | 6 | 9 | 11 if day > 0 && day < 31 => true,
122 2 if is_leap_year(year) && day > 0 && day < 30 => true,
123 2 if !is_leap_year(year) && day > 0 && day < 29 => true,
124 _ => false,
125 }
126 }
127}
128
129fn is_leap_year(year: u32) -> bool {
130 if year % 4 == 0 {
131 if year % 100 == 0 {
132 if year % 400 == 0 {
133 true
134 } else {
135 false
136 }
137 } else {
138 true
139 }
140 } else {
141 false
142 }
143}
144
145#[cfg(test)]
146mod tests {
147
148 use super::*;
149
150 #[test]
151 fn bool_works() {
152 let v: Vec<&str> = vec!["true", "True", "TRUE", "false", "False", "FALSE"];
153 let exp: Vec<DataType> = vec![
154 DataType::Bool(true),
155 DataType::Bool(true),
156 DataType::Bool(true),
157 DataType::Bool(false),
158 DataType::Bool(false),
159 DataType::Bool(false),
160 ];
161 for (i, el) in v.iter().enumerate() {
162 let result = check_type(el);
163 assert_eq!(result, exp[i]);
164 }
165 }
166
167 #[test]
168 fn int_works() {
169 let v: Vec<&str> = vec!["123", "0123", "465.0", "-34.0", "-27", "000", "0", "0.0"];
170 let exp: Vec<DataType> = vec![
171 DataType::Int(123),
172 DataType::Int(123),
173 DataType::Int(465),
174 DataType::Int(-34),
175 DataType::Int(-27),
176 DataType::Int(0),
177 DataType::Int(0),
178 DataType::Int(0),
179 ];
180 for (i, el) in v.iter().enumerate() {
181 let result = check_type(el);
182 assert_eq!(result, exp[i]);
183 }
184 }
185
186 #[test]
187 fn float_works() {
188 let v: Vec<&str> = vec![
189 "123.1", "0123.2", "465.389", "-34.2", "-27.99", "000.1", "0.00001", "-.2", ".324",
190 ];
191 let exp: Vec<DataType> = vec![
192 DataType::Float(123.1),
193 DataType::Float(123.2),
194 DataType::Float(465.389),
195 DataType::Float(-34.2),
196 DataType::Float(-27.99),
197 DataType::Float(0.1),
198 DataType::Float(0.00001),
199 DataType::Float(-0.2),
200 DataType::Float(0.324),
201 ];
202 for (i, el) in v.iter().enumerate() {
203 let result = check_type(el);
204 assert_eq!(result, exp[i]);
205 }
206 }
207
208 #[test]
209 fn datetime_works() {
210 let v: Vec<&str> = vec!["20220405", "20221213", "2014-11-28T12:00:09Z"];
211 let exp: Vec<DataType> = vec![
212 DataType::DateTime(Utc.ymd(2022, 4, 5).and_hms(0, 0, 0)),
213 DataType::DateTime(Utc.ymd(2022, 12, 13).and_hms(0, 0, 0)),
214 DataType::DateTime(Utc.ymd(2014, 11, 28).and_hms(12, 0, 9)),
215 ];
216 for (i, el) in v.iter().enumerate() {
217 let result = check_type(el);
218 assert_eq!(result, exp[i]);
219 }
220 }
221
222 #[test]
223 fn string_works() {
224 let v: Vec<&str> = vec!["fdsaf", "0.3213-", "2014-1111"];
225 let exp: Vec<DataType> = vec![
226 DataType::String("fdsaf"),
227 DataType::String("0.3213-"),
228 DataType::String("2014-1111"),
229 ];
230 for (i, el) in v.iter().enumerate() {
231 let result = check_type(el);
232 assert_eq!(result, exp[i]);
233 }
234 }
235}