1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#![allow(dead_code)]
#![allow(unused)]
pub const MICROS_PER_MILLIS: u32 = 1000;
pub const NANOS_PER_MICRO: u32 = 1000;
pub const TICKS_PER_MICROSECOND: u32 = 10;
pub const NANOSECONDS_PER_TICK: u32 = 100;

// https://docs.rs/chrono/latest/chrono/format/strftime/index.html
pub const DATE_TIME_FORMAT_WITH_NANOSECONDS_LEN: usize = 8 + 1 + 6 + 2 + 1 + 9;
pub const DATE_TIME_FORMAT_WITH_NANOSECONDS: &str = "%Y%m%d-%H:%M:%S.%f";
pub const DATE_TIME_FORMAT_WITH_MICROSECONDS_LEN: usize = 8 + 1 + 6 + 2 + 1 + 6;
pub const DATE_TIME_FORMAT_WITH_MICROSECONDS: &str = "%Y%m%d-%H:%M:%S.%6f";
pub const DATE_TIME_FORMAT_WITH_MILLISECONDS_LEN: usize = 8 + 1 + 6 + 2 + 1 + 3;
pub const DATE_TIME_FORMAT_WITH_MILLISECONDS: &str = "%Y%m%d-%H:%M:%S.%3f";
pub const DATE_TIME_FORMAT_WITHOUT_MILLISECONDS_LEN: usize = 8 + 1 + 6 + 2;
pub const DATE_TIME_FORMAT_WITHOUT_MILLISECONDS: &str = "%Y%m%d-%H:%M:%S";
pub const DATE_ONLY_FORMAT: &str = "%Y%m%d";
pub const TIME_ONLY_FORMAT_WITH_NANOSECONDS: &str = "%H:%M:%S.%f";
pub const TIME_ONLY_FORMAT_WITH_MICROSECONDS: &str = "%H:%M:%S.%6f";
pub const TIME_ONLY_FORMAT_WITH_MILLISECONDS: &str = "%H:%M:%S.%3f";
pub const TIME_ONLY_FORMAT_WITHOUT_MILLISECONDS: &str = "%H:%M:%S";

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DateTimeFormat {
    Nanoseconds,
    Microseconds,
    Milliseconds,
    Seconds,
}

impl DateTimeFormat {
    pub fn as_datetime_format(&self) -> &str {
        match self {
            DateTimeFormat::Nanoseconds => DATE_TIME_FORMAT_WITH_NANOSECONDS,
            DateTimeFormat::Microseconds => DATE_TIME_FORMAT_WITH_MICROSECONDS,
            DateTimeFormat::Milliseconds => DATE_TIME_FORMAT_WITH_MILLISECONDS,
            DateTimeFormat::Seconds => DATE_TIME_FORMAT_WITHOUT_MILLISECONDS,
        }
    }
    pub fn as_time_format(&self) -> &str {
        match self {
            DateTimeFormat::Nanoseconds => TIME_ONLY_FORMAT_WITH_NANOSECONDS,
            DateTimeFormat::Microseconds => TIME_ONLY_FORMAT_WITH_MICROSECONDS,
            DateTimeFormat::Milliseconds => TIME_ONLY_FORMAT_WITH_MILLISECONDS,
            DateTimeFormat::Seconds => TIME_ONLY_FORMAT_WITHOUT_MILLISECONDS,
        }
    }
}

impl std::convert::TryFrom<String> for DateTimeFormat {
    type Error = &'static str;

    fn try_from(value: String) -> Result<Self, Self::Error> {
        match value.as_str() {
            "Nanoseconds" => Ok(Self::Nanoseconds),
            "Microseconds" => Ok(Self::Microseconds),
            "Milliseconds" => Ok(Self::Milliseconds),
            "Seconds" => Ok(Self::Seconds),
            _ => Err("Valid format is ... TODO")
        }
    }
}

use chrono::{DateTime as ChronoDateTime, TimeZone, Utc, NaiveDateTime, NaiveDate, NaiveTime, Local};

use crate::field_map::FieldValue;
use crate::fields::converters::TryFrom;
use crate::fields::ConversionError;

use super::IntoBytes;

impl<'a> TryFrom<&'a FieldValue> for ChronoDateTime<Utc> {
    type Error = ConversionError;

    fn try_from(value: &'a FieldValue) -> Result<Self, Self::Error> {
        let time: &str = TryFrom::try_from(value)?;
        match Utc.datetime_from_str(time, DATE_TIME_FORMAT_WITHOUT_MILLISECONDS) {
            Ok(t) => Ok(t),
            Err(e) => match Utc.datetime_from_str(time, DATE_TIME_FORMAT_WITH_MILLISECONDS) {
                Ok(t) => Ok(t),
                Err(_) => todo!(),
            },
        }
    }
}

pub type Date = NaiveDate;
pub type Time = NaiveTime;
pub type DateTime = NaiveDateTime;

impl<'a> TryFrom<&'a FieldValue> for DateTime {
    type Error = ConversionError;

    fn try_from(value: &'a FieldValue) -> Result<Self, Self::Error> {
        let time: &str = TryFrom::try_from(value)?;
        let format = match time.len() {
            DATE_TIME_FORMAT_WITH_NANOSECONDS_LEN => Ok(DATE_TIME_FORMAT_WITH_NANOSECONDS),
            DATE_TIME_FORMAT_WITH_MICROSECONDS_LEN => Ok(DATE_TIME_FORMAT_WITH_MICROSECONDS),
            DATE_TIME_FORMAT_WITH_MILLISECONDS_LEN => Ok(DATE_TIME_FORMAT_WITH_MILLISECONDS),
            DATE_TIME_FORMAT_WITHOUT_MILLISECONDS_LEN => Ok(DATE_TIME_FORMAT_WITHOUT_MILLISECONDS),
            len => Err(ConversionError::EncodingError),
        }?;
        match NaiveDateTime::parse_from_str(time, format) {
            Ok(v) => Ok(v),
            Err(e) => todo!("{e}"),
        }
    }
}

impl<'a> TryFrom<&'a FieldValue> for Time {
    type Error = ConversionError;

    fn try_from(value: &'a FieldValue) -> Result<Self, Self::Error> {
        todo!()
    }
}

impl<'a> TryFrom<&'a FieldValue> for Date {
    type Error = ConversionError;

    fn try_from(value: &'a FieldValue) -> Result<Self, Self::Error> {
        todo!()
    }
}

impl IntoBytes<FieldValue> for DateTime {
    fn as_bytes(&self) -> FieldValue {
        todo!()
    }
}

impl IntoBytes<FieldValue> for Date {
    fn as_bytes(&self) -> FieldValue {
        todo!()
    }
}

impl IntoBytes<FieldValue> for Time {
    fn as_bytes(&self) -> FieldValue {
        todo!()
    }
}