type_checker/
lib.rs

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    // pure numbers
82    if let Some(_) = try_get_i64(s) {
83        // yyyyMMdd
84        match s.len() {
85            // yyyyMMdd
86            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}