ledger_models/fintekkers/wrappers/models/utils/
date.rs

1use chrono::naive::NaiveDate;
2use chrono::{DateTime, Datelike, TimeZone, Utc};
3use crate::fintekkers::models::util::LocalDateProto;
4use std::fmt;
5use std::str::FromStr;
6use crate::fintekkers::wrappers::models::utils::errors::Error;
7
8static DATE_FORMAT: &str = "%Y-%m-%d";
9
10pub struct DateWrapper {
11    proto: LocalDateProto,
12}
13
14impl AsRef<LocalDateProto> for DateWrapper {
15    fn as_ref(&self) -> &LocalDateProto {
16        &self.proto
17    }
18}
19
20impl DateWrapper {
21    pub fn new(proto: LocalDateProto) -> Self {
22        DateWrapper { proto }
23    }
24}
25
26#[derive(Debug)]
27pub enum ParseError {
28    NotValidFormat,
29}
30
31impl FromStr for DateWrapper {
32    type Err = ParseError;
33
34    // Parses a date from the yyyy-mm-dd format
35    fn from_str(value: &str) -> Result<Self, Self::Err> {
36        let naive_date = NaiveDate::parse_from_str(value, DATE_FORMAT)
37            .unwrap_or_else(
38                |_| panic!("Date must be in the format of {}. Received {}", DATE_FORMAT, value));
39
40        Ok(DateWrapper {
41            proto: LocalDateProto {
42                year: naive_date.year() as u32,
43                month: naive_date.month(),
44                day: naive_date.day(),
45            },
46        })
47    }
48}
49
50impl TryFrom<DateWrapper> for DateTime<Utc> {
51    type Error = Error;
52
53    fn try_from(value: DateWrapper) -> Result<DateTime<Utc>, Error> {
54        let year = value.proto.year as i32;
55        let month = value.proto.month;
56        let day = value.proto.day;
57
58        Utc.with_ymd_and_hms(year, month, day, 0, 0, 0)
59            .single()
60            .ok_or(Error::DateConversion)
61    }
62}
63
64
65impl TryFrom<DateWrapper> for NaiveDate {
66    type Error = Error;
67
68    fn try_from(value: DateWrapper) -> Result<NaiveDate, Error> {
69        let year = value.proto.year as i32;
70        let month = value.proto.month;
71        let day = value.proto.day;
72
73        NaiveDate::from_ymd_opt(year, month, day).ok_or(Error::DateConversion)
74    }
75}
76
77impl From<DateWrapper> for LocalDateProto {
78    fn from(value: DateWrapper) -> Self {
79        value.proto
80    }
81}
82
83impl fmt::Display for DateWrapper {
84    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
85        let date_str = &format!(
86            "{}-{}-{}",
87            &self.proto.year, &self.proto.month, &self.proto.day
88        );
89        fmt.write_str(date_str).unwrap();
90        Ok(())
91    }
92}
93
94#[cfg(test)]
95mod test {
96    use super::*;
97
98    #[test]
99    fn test_proto_to_date() {
100        let input = DateWrapper {
101            proto: LocalDateProto {
102                year: 2020,
103                month: 12,
104                day: 31,
105            },
106        };
107
108        let output: NaiveDate = input.try_into().expect("failed to convert date");
109
110        assert_eq!(output.to_string(), "2020-12-31");
111    }
112
113    #[test]
114    fn test_date_from_string() {
115        let date = DateWrapper::from_str("2023-10-28").unwrap();
116        assert_eq!(date.proto.year, 2023);
117    }
118}