crypto_pay_api/models/stats/
builder.rs

1use chrono::{DateTime, Utc};
2
3use crate::{
4    error::{CryptoBotError, CryptoBotResult, ValidationErrorKind},
5    validation::FieldValidate,
6};
7
8use super::GetStatsParams;
9
10#[derive(Debug, Default)]
11pub struct GetStatsParamsBuilder {
12    start_at: Option<DateTime<Utc>>,
13    end_at: Option<DateTime<Utc>>,
14}
15
16impl GetStatsParamsBuilder {
17    /// Create a new `GetStatsParamsBuilder` with default values.
18    pub fn new() -> Self {
19        Self::default()
20    }
21
22    /// Set the start date for the statistics.
23    /// Optional. Defaults is current date minus 24 hours.
24    pub fn start_at(mut self, start_at: DateTime<Utc>) -> Self {
25        self.start_at = Some(start_at);
26        self
27    }
28
29    /// Set the end date for the statistics.
30    /// Optional. Defaults is current date.
31    pub fn end_at(mut self, end_at: DateTime<Utc>) -> Self {
32        self.end_at = Some(end_at);
33        self
34    }
35}
36
37impl FieldValidate for GetStatsParamsBuilder {
38    fn validate(&self) -> CryptoBotResult<()> {
39        let now = Utc::now();
40
41        if let Some(start) = self.start_at {
42            if start > now {
43                return Err(CryptoBotError::ValidationError {
44                    kind: ValidationErrorKind::Range,
45                    message: "start_at cannot be in the future".to_string(),
46                    field: Some("start_at".to_string()),
47                });
48            }
49        }
50
51        if let (Some(start), Some(end)) = (self.start_at, self.end_at) {
52            if end < start {
53                return Err(CryptoBotError::ValidationError {
54                    kind: ValidationErrorKind::Range,
55                    message: "end_at cannot be earlier than start_at".to_string(),
56                    field: Some("end_at".to_string()),
57                });
58            }
59        }
60
61        Ok(())
62    }
63}
64
65impl GetStatsParamsBuilder {
66    pub fn build(self) -> CryptoBotResult<GetStatsParams> {
67        self.validate()?;
68        Ok(GetStatsParams {
69            start_at: self.start_at,
70            end_at: self.end_at,
71        })
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_get_stats_params_builder() {
81        let params = GetStatsParamsBuilder::new().start_at(Utc::now()).build();
82
83        assert!(params.is_ok());
84    }
85
86    #[test]
87    fn test_get_stats_params_builder_invalid_start_at() {
88        let params = GetStatsParamsBuilder::new()
89            .start_at(Utc::now() + chrono::Duration::days(1))
90            .build();
91
92        assert!(matches!(
93            params,
94            Err(CryptoBotError::ValidationError {
95                kind: ValidationErrorKind::Range,
96                field: Some(field),
97                ..
98            }) if field == "start_at"
99        ));
100    }
101    #[test]
102    fn test_get_stats_params_builder_invalid_end_at() {
103        let params = GetStatsParamsBuilder::new()
104            .start_at(Utc::now())
105            .end_at(Utc::now() - chrono::Duration::days(1))
106            .build();
107
108        assert!(matches!(
109            params,
110            Err(CryptoBotError::ValidationError {
111                kind: ValidationErrorKind::Range,
112                field: Some(field),
113                ..
114            }) if field == "end_at"
115        ));
116    }
117
118    #[test]
119    fn test_get_stats_params_builder_invalid_end_at_and_start_at() {
120        let params = GetStatsParamsBuilder::new()
121            .start_at(Utc::now() - chrono::Duration::days(2))
122            .end_at(Utc::now() - chrono::Duration::days(3))
123            .build();
124
125        assert!(matches!(
126            params,
127            Err(CryptoBotError::ValidationError {
128                kind: ValidationErrorKind::Range,
129                field: Some(field),
130                ..
131            }) if field == "end_at"
132        ));
133    }
134}