1use std::fmt;
2
3#[cfg(feature = "dates")]
4use once_cell::sync::OnceCell;
5use serde::de::Visitor;
6use serde::{self, Deserialize};
7
8use super::CellErrorType;
9
10#[cfg(feature = "dates")]
11static EXCEL_EPOCH: OnceCell<chrono::NaiveDateTime> = OnceCell::new();
12
13#[cfg(feature = "dates")]
14const MS_MULTIPLIER: f64 = 24f64 * 60f64 * 60f64 * 1e+3f64;
15
16#[derive(Debug, Clone, PartialEq, Default)]
19pub enum DataType {
20 Int(i64),
22 Float(f64),
24 String(String),
26 Bool(bool),
28 DateTime(f64),
30 Duration(f64),
32 DateTimeIso(String),
34 DurationIso(String),
36 Error(CellErrorType),
38 #[default]
40 Empty,
41}
42
43impl DataType {
44 pub fn is_empty(&self) -> bool {
46 *self == DataType::Empty
47 }
48 pub fn is_int(&self) -> bool {
50 matches!(*self, DataType::Int(_))
51 }
52 pub fn is_float(&self) -> bool {
54 matches!(*self, DataType::Float(_))
55 }
56 pub fn is_bool(&self) -> bool {
58 matches!(*self, DataType::Bool(_))
59 }
60 pub fn is_string(&self) -> bool {
62 matches!(*self, DataType::String(_))
63 }
64
65 pub fn get_int(&self) -> Option<i64> {
67 if let DataType::Int(v) = self {
68 Some(*v)
69 } else {
70 None
71 }
72 }
73 pub fn get_float(&self) -> Option<f64> {
75 if let DataType::Float(v) = self {
76 Some(*v)
77 } else {
78 None
79 }
80 }
81 pub fn get_bool(&self) -> Option<bool> {
83 if let DataType::Bool(v) = self {
84 Some(*v)
85 } else {
86 None
87 }
88 }
89 pub fn get_string(&self) -> Option<&str> {
91 if let DataType::String(v) = self {
92 Some(&**v)
93 } else {
94 None
95 }
96 }
97
98 pub fn as_string(&self) -> Option<String> {
100 match self {
101 DataType::Float(v) => Some(v.to_string()),
102 DataType::Int(v) => Some(v.to_string()),
103 DataType::String(v) => Some(v.clone()),
104 _ => None,
105 }
106 }
107 pub fn as_i64(&self) -> Option<i64> {
109 match self {
110 DataType::Int(v) => Some(*v),
111 DataType::Float(v) => Some(*v as i64),
112 DataType::String(v) => v.parse::<i64>().ok(),
113 _ => None,
114 }
115 }
116 pub fn as_f64(&self) -> Option<f64> {
118 match self {
119 DataType::Int(v) => Some(*v as f64),
120 DataType::Float(v) => Some(*v),
121 DataType::String(v) => v.parse::<f64>().ok(),
122 _ => None,
123 }
124 }
125 #[cfg(feature = "dates")]
127 pub fn as_date(&self) -> Option<chrono::NaiveDate> {
128 use std::str::FromStr;
129 match self {
130 DataType::DateTimeIso(s) => self
131 .as_datetime()
132 .map(|dt| dt.date())
133 .or_else(|| chrono::NaiveDate::from_str(s).ok()),
134 _ => self.as_datetime().map(|dt| dt.date()),
135 }
136 }
137
138 #[cfg(feature = "dates")]
140 pub fn as_time(&self) -> Option<chrono::NaiveTime> {
141 use std::str::FromStr;
142 match self {
143 DataType::DateTimeIso(s) => self
144 .as_datetime()
145 .map(|dt| dt.time())
146 .or_else(|| chrono::NaiveTime::from_str(s).ok()),
147 DataType::DurationIso(s) => chrono::NaiveTime::parse_from_str(s, "PT%HH%MM%S%.fS").ok(),
148 _ => self.as_datetime().map(|dt| dt.time()),
149 }
150 }
151
152 #[cfg(feature = "dates")]
154 pub fn as_duration(&self) -> Option<chrono::Duration> {
155 use chrono::Timelike;
156
157 match self {
158 DataType::Duration(days) => {
159 let ms = days * MS_MULTIPLIER;
160 Some(chrono::Duration::milliseconds(ms.round() as i64))
161 }
162 DataType::DurationIso(_) => self.as_time().map(|t| {
165 chrono::Duration::nanoseconds(
166 t.num_seconds_from_midnight() as i64 * 1_000_000_000 + t.nanosecond() as i64,
167 )
168 }),
169 _ => None,
170 }
171 }
172
173 #[cfg(feature = "dates")]
175 pub fn as_datetime(&self) -> Option<chrono::NaiveDateTime> {
176 use std::str::FromStr;
177
178 match self {
179 DataType::Int(x) => {
180 let days = x - 25569;
181 let secs = days * 86400;
182 chrono::NaiveDateTime::from_timestamp_opt(secs, 0)
183 }
184 DataType::Float(f) | DataType::DateTime(f) => {
185 let excel_epoch = EXCEL_EPOCH.get_or_init(|| {
186 chrono::NaiveDate::from_ymd_opt(1899, 12, 30)
187 .unwrap()
188 .and_hms_opt(0, 0, 0)
189 .unwrap()
190 });
191 let f = if *f >= 60.0 { *f } else { *f + 1.0 };
192 let ms = f * MS_MULTIPLIER;
193 let excel_duration = chrono::Duration::milliseconds(ms.round() as i64);
194 excel_epoch.checked_add_signed(excel_duration)
195 }
196 DataType::DateTimeIso(s) => chrono::NaiveDateTime::from_str(s).ok(),
197 _ => None,
198 }
199 }
200}
201
202impl PartialEq<&str> for DataType {
203 fn eq(&self, other: &&str) -> bool {
204 match *self {
205 DataType::String(ref s) if s == other => true,
206 _ => false,
207 }
208 }
209}
210
211impl PartialEq<str> for DataType {
212 fn eq(&self, other: &str) -> bool {
213 matches!(*self, DataType::String(ref s) if s == other)
214 }
215}
216
217impl PartialEq<f64> for DataType {
218 fn eq(&self, other: &f64) -> bool {
219 matches!(*self, DataType::Float(ref s) if *s == *other)
220 }
221}
222
223impl PartialEq<bool> for DataType {
224 fn eq(&self, other: &bool) -> bool {
225 matches!(*self, DataType::Bool(ref s) if *s == *other)
226 }
227}
228
229impl PartialEq<i64> for DataType {
230 fn eq(&self, other: &i64) -> bool {
231 matches!(*self, DataType::Int(ref s) if *s == *other)
232 }
233}
234
235impl fmt::Display for DataType {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
237 match *self {
238 DataType::Int(ref e) => write!(f, "{}", e),
239 DataType::Float(ref e) => write!(f, "{}", e),
240 DataType::String(ref e) => write!(f, "{}", e),
241 DataType::Bool(ref e) => write!(f, "{}", e),
242 DataType::DateTime(ref e) => write!(f, "{}", e),
243 DataType::Duration(ref e) => write!(f, "{}", e),
244 DataType::DateTimeIso(ref e) => write!(f, "{}", e),
245 DataType::DurationIso(ref e) => write!(f, "{}", e),
246 DataType::Error(ref e) => write!(f, "{}", e),
247 DataType::Empty => Ok(()),
248 }
249 }
250}
251
252impl<'de> Deserialize<'de> for DataType {
253 #[inline]
254 fn deserialize<D>(deserializer: D) -> Result<DataType, D::Error>
255 where
256 D: serde::Deserializer<'de>,
257 {
258 struct DataTypeVisitor;
259
260 impl<'de> Visitor<'de> for DataTypeVisitor {
261 type Value = DataType;
262
263 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
264 formatter.write_str("any valid JSON value")
265 }
266
267 #[inline]
268 fn visit_bool<E>(self, value: bool) -> Result<DataType, E> {
269 Ok(DataType::Bool(value))
270 }
271
272 #[inline]
273 fn visit_i64<E>(self, value: i64) -> Result<DataType, E> {
274 Ok(DataType::Int(value))
275 }
276
277 #[inline]
278 fn visit_u64<E>(self, value: u64) -> Result<DataType, E> {
279 Ok(DataType::Int(value as i64))
280 }
281
282 #[inline]
283 fn visit_f64<E>(self, value: f64) -> Result<DataType, E> {
284 Ok(DataType::Float(value))
285 }
286
287 #[inline]
288 fn visit_str<E>(self, value: &str) -> Result<DataType, E>
289 where
290 E: serde::de::Error,
291 {
292 self.visit_string(String::from(value))
293 }
294
295 #[inline]
296 fn visit_string<E>(self, value: String) -> Result<DataType, E> {
297 Ok(DataType::String(value))
298 }
299
300 #[inline]
301 fn visit_none<E>(self) -> Result<DataType, E> {
302 Ok(DataType::Empty)
303 }
304
305 #[inline]
306 fn visit_some<D>(self, deserializer: D) -> Result<DataType, D::Error>
307 where
308 D: serde::Deserializer<'de>,
309 {
310 Deserialize::deserialize(deserializer)
311 }
312
313 #[inline]
314 fn visit_unit<E>(self) -> Result<DataType, E> {
315 Ok(DataType::Empty)
316 }
317 }
318
319 deserializer.deserialize_any(DataTypeVisitor)
320 }
321}
322
323macro_rules! define_from {
324 ($variant:path, $ty:ty) => {
325 impl From<$ty> for DataType {
326 fn from(v: $ty) -> Self {
327 $variant(v)
328 }
329 }
330 };
331}
332
333define_from!(DataType::Int, i64);
334define_from!(DataType::Float, f64);
335define_from!(DataType::String, String);
336define_from!(DataType::Bool, bool);
337define_from!(DataType::Error, CellErrorType);
338
339impl<'a> From<&'a str> for DataType {
340 fn from(v: &'a str) -> Self {
341 DataType::String(String::from(v))
342 }
343}
344
345impl From<()> for DataType {
346 fn from(_: ()) -> Self {
347 DataType::Empty
348 }
349}
350
351impl<T> From<Option<T>> for DataType
352where
353 DataType: From<T>,
354{
355 fn from(v: Option<T>) -> Self {
356 match v {
357 Some(v) => From::from(v),
358 None => DataType::Empty,
359 }
360 }
361}
362
363#[cfg(all(test, feature = "dates"))]
364mod date_tests {
365 use super::*;
366
367 #[test]
368 fn test_dates() {
369 use chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime};
370
371 let unix_epoch = DataType::Float(25569.);
372 assert_eq!(
373 unix_epoch.as_datetime(),
374 Some(NaiveDateTime::new(
375 NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
376 NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
377 ))
378 );
379
380 let unix_epoch_precision = DataType::Float(44484.7916666667);
382 assert_eq!(
383 unix_epoch_precision.as_datetime(),
384 Some(NaiveDateTime::new(
385 NaiveDate::from_ymd_opt(2021, 10, 15).unwrap(),
386 NaiveTime::from_hms_opt(19, 0, 0).unwrap(),
387 ))
388 );
389
390 assert_eq!(
392 DataType::Float(0.18737500000000001).as_time(),
393 Some(NaiveTime::from_hms_milli_opt(4, 29, 49, 200).unwrap())
394 );
395 assert_eq!(
396 DataType::Float(0.25951736111111101).as_time(),
397 Some(NaiveTime::from_hms_milli_opt(6, 13, 42, 300).unwrap())
398 );
399
400 assert_eq!(DataType::Float(1e20).as_time(), None);
402
403 let unix_epoch_15h30m = DataType::Float(25569.645833333333333);
404 let chrono_dt = NaiveDateTime::new(
405 NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
406 NaiveTime::from_hms_opt(15, 30, 0).unwrap(),
407 );
408 let micro = Duration::microseconds(1);
409 assert!(unix_epoch_15h30m.as_datetime().unwrap() - chrono_dt < micro);
410 }
411
412 #[test]
413 fn test_int_dates() {
414 use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
415
416 let unix_epoch = DataType::Int(25569);
417 assert_eq!(
418 unix_epoch.as_datetime(),
419 Some(NaiveDateTime::new(
420 NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(),
421 NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
422 ))
423 );
424
425 let time = DataType::Int(44060);
426 assert_eq!(
427 time.as_datetime(),
428 Some(NaiveDateTime::new(
429 NaiveDate::from_ymd_opt(2020, 8, 17).unwrap(),
430 NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
431 ))
432 );
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 #[test]
441 fn test_partial_eq() {
442 assert_eq!(DataType::String("value".to_string()), "value");
443 assert_eq!(DataType::String("value".to_string()), "value"[..]);
444 assert_eq!(DataType::Float(100.0), 100.0f64);
445 assert_eq!(DataType::Bool(true), true);
446 assert_eq!(DataType::Int(100), 100i64);
447 }
448}