brasilapi_client/definitions/
holidays.rs

1use crate::{client::*, constants::holidays::{SVC_URL,MIN_YEAR, MAX_YEAR}, errors::*, request::*};
2use serde::{Deserialize, Serialize};
3
4// Defining a custom serializer for only Dates
5mod serde_naivedate {
6    use serde::{self, Deserialize, Serializer, Deserializer};
7
8    const FORMAT: &'static str = "%Y-%m-%d";
9
10    pub fn serialize<S>(
11        date: &chrono::NaiveDate,
12        serializer: S,
13    ) -> Result<S::Ok, S::Error>
14    where
15        S: Serializer,
16    {
17        let s = format!("{}", date.format(FORMAT));
18        serializer.serialize_str(&s)
19    }
20
21    pub fn deserialize<'de, D>(
22        deserializer: D,
23    ) -> Result<chrono::NaiveDate, D::Error>
24    where
25        D: Deserializer<'de>,
26    {
27        let s = String::deserialize(deserializer)?;
28        chrono::NaiveDate::parse_from_str(&s, FORMAT).map_err(serde::de::Error::custom)
29    }
30}
31
32#[derive(Serialize, Deserialize, Debug)]
33#[serde(rename_all = "camelCase")]
34/**
35Data contract for Brasil holidays
36 */
37pub struct HolidaysResponseData {
38    /// The holiday date
39    #[serde(with = "serde_naivedate")]
40    pub date : chrono::NaiveDate,
41    /// The holiday name
42    pub name: String,
43    /// The holiday type
44    pub r#type : String
45}
46
47impl PartialEq for HolidaysResponseData {
48
49    fn eq(&self, other: &Self) -> bool {
50        self.date == other.date && self.name == other.name && self.r#type == other.r#type
51    }
52}
53
54impl BrasilApiClient {
55    pub async fn get_holidays(&self, year: &i32) -> Result<Vec<HolidaysResponseData>, Error> {
56        if year < MIN_YEAR || year > MAX_YEAR {
57            return Err(Error::InvalidInputRangeError
58                {
59                    name: "year".to_string(),
60                    min: *MIN_YEAR, 
61                    max: *MAX_YEAR
62                })
63        }
64
65        
66        Ok(get::<(), Vec<HolidaysResponseData>>(
67            &format!("{}/{}/{}", self.base_url, SVC_URL, year)
68        ).await?)
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use crate::{holidays::{HolidaysResponseData}, client::tests::cli};
75
76    use super::*;
77    use futures_await_test::async_test;
78
79    #[async_test]
80    async fn test_invalid_min_range() {
81        let year = MIN_YEAR - 1;
82        let resp = cli().get_holidays(&year).await;
83        assert!(resp.is_err());
84    }
85
86    #[async_test]
87    async fn test_invalid_max_range() {
88        let year = MAX_YEAR + 1;
89        let resp = cli().get_holidays(&year).await;
90        assert!(resp.is_err());
91    }
92
93    #[async_test]
94    async fn test_valid_at_1900() {
95        let year = MIN_YEAR;
96        let resp = cli().get_holidays(year).await;
97        assert!(resp.is_ok());
98        
99        let expected_resp = r#"[{"date":"1900-01-01","name":"Confraternização mundial","type":"national"},{"date":"1900-02-27","name":"Carnaval","type":"national"},{"date":"1900-04-15","name":"Páscoa","type":"national"},{"date":"1900-04-21","name":"Tiradentes","type":"national"},{"date":"1900-05-01","name":"Dia do trabalho","type":"national"},{"date":"1900-06-14","name":"Corpus Christi","type":"national"},{"date":"1900-09-07","name":"Independência do Brasil","type":"national"},{"date":"1900-10-12","name":"Nossa Senhora Aparecida","type":"national"},{"date":"1900-11-02","name":"Finados","type":"national"},{"date":"1900-11-15","name":"Proclamação da República","type":"national"},{"date":"1900-12-25","name":"Natal","type":"national"}]"#;
100        let expected =  serde_json::from_str::<Vec<HolidaysResponseData>>(expected_resp).unwrap();
101        let received = resp.unwrap();
102        assert_eq!(expected.len(), received.len());
103
104        let difference:bool = received.iter().any(|item| !expected.contains(item));
105        assert!(!difference);
106    }
107}