zero_bounce/utility/structures/
generic.rs

1use chrono::NaiveDate;
2use serde::Deserialize;
3
4use crate::utility::structures::custom_deserialize::deserialize_only_date;
5use crate::utility::structures::custom_deserialize::deserialize_stringified_uint;
6
7#[derive(Clone, Debug, Deserialize)]
8pub struct ApiUsage {
9    pub total: u64,
10    pub status_valid: u64,
11    pub status_invalid: u64,
12    pub status_catch_all: u64,
13    pub status_do_not_mail: u64,
14    pub status_spamtrap: u64,
15    pub status_unknown: u64,
16    pub sub_status_toxic: u64,
17    pub sub_status_disposable: u64,
18    pub sub_status_role_based: u64,
19    pub sub_status_possible_trap: u64,
20    pub sub_status_global_suppression: u64,
21    pub sub_status_timeout_exceeded: u64,
22    pub sub_status_mail_server_temporary_error: u64,
23    pub sub_status_mail_server_did_not_respond: u64,
24    pub sub_status_greylisted: u64,
25    pub sub_status_antispam_system: u64,
26    pub sub_status_does_not_accept_mail: u64,
27    pub sub_status_exception_occurred: u64,
28    pub sub_status_failed_syntax_check: u64,
29    pub sub_status_mailbox_not_found: u64,
30    pub sub_status_unroutable_ip_address: u64,
31    pub sub_status_possible_typo: u64,
32    pub sub_status_no_dns_entries: u64,
33    pub sub_status_role_based_catch_all: u64,
34    pub sub_status_accept_all: u64,
35    pub sub_status_mailbox_quota_exceeded: u64,
36    pub sub_status_forcible_disconnect: u64,
37    pub sub_status_failed_smtp_connection: u64,
38    pub sub_status_mx_forward: u64,
39
40    #[serde(deserialize_with="deserialize_only_date")]
41    pub start_date: NaiveDate,
42
43    #[serde(deserialize_with="deserialize_only_date")]
44    pub end_date: NaiveDate,
45}
46
47#[derive(Clone, Debug, Deserialize)]
48pub struct ActivityData {
49    pub found: bool,
50
51    #[serde(deserialize_with="deserialize_stringified_uint")]
52    pub active_in_days: Option<u128>,
53}
54
55#[derive(Clone, Debug, Deserialize)]
56pub struct DomainFormats {
57    pub format: String,
58    pub confidence: String,
59}
60
61#[derive(Clone, Debug, Deserialize)]
62pub struct FindEmailResponse {
63    pub email: String,
64    pub domain: String,
65    pub format: String,
66    pub status: String,
67    pub sub_status: String,
68    pub confidence: String,
69    pub did_you_mean: String,
70    pub failure_reason: String,
71    pub other_domain_formats: Vec<DomainFormats>,
72}
73
74/// Response structure for the new find_email_v2 API endpoint.
75/// This structure matches the new API response format which includes
76/// `email_confidence` and `company_name` fields.
77#[derive(Clone, Debug, Deserialize)]
78pub struct FindEmailResponseV2 {
79    pub email: String,
80    #[serde(default)]
81    pub domain: String,
82    #[serde(rename = "email_confidence", default)]
83    pub confidence: String,
84    #[serde(default)]
85    pub company_name: String,
86    #[serde(default)]
87    pub did_you_mean: String,
88    #[serde(default)]
89    pub failure_reason: String,
90}
91
92/// Response structure for the new domain_search_v2 API endpoint.
93/// This structure matches the domain search API response format which includes
94/// `format`, `confidence`, and `other_domain_formats` fields.
95#[derive(Clone, Debug, Deserialize)]
96pub struct DomainSearchResponseV2 {
97    #[serde(default)]
98    pub domain: String,
99    #[serde(default)]
100    pub company_name: String,
101    #[serde(default)]
102    pub format: String,
103    #[serde(default)]
104    pub confidence: String,
105    #[serde(default)]
106    pub did_you_mean: String,
107    #[serde(default)]
108    pub failure_reason: String,
109    #[serde(default)]
110    pub other_domain_formats: Vec<DomainFormats>,
111}
112
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117    use crate::utility::mock_constants::API_USAGE_RESPONSE;
118    use crate::utility::mock_constants::ACTIVITY_DATA_RESPONSE_ACTIVE;
119    use crate::utility::mock_constants::ACTIVITY_DATA_RESPONSE_INACTIVE;
120    use crate::utility::mock_constants::MOCK_FIND_MAIL_INVALID;
121    use crate::utility::mock_constants::MOCK_FIND_MAIL_VALID;
122
123    #[test]
124    fn parse_activity_date_without_amount() {
125        let activity_data_res = serde_json::from_str::<ActivityData>(
126            ACTIVITY_DATA_RESPONSE_INACTIVE
127        );
128        assert!(activity_data_res.is_ok(), "error: {}", activity_data_res.unwrap_err());
129
130        let activity_data = activity_data_res.unwrap();
131        assert_eq!(activity_data.found, false);
132        assert_eq!(activity_data.active_in_days, None);
133    }
134
135    #[test]
136    fn parse_activity_date_with_amount() {
137        let activity_data_res: serde_json::Result<ActivityData> = serde_json::from_str(ACTIVITY_DATA_RESPONSE_ACTIVE);
138        assert!(activity_data_res.is_ok());
139
140        let activity_data = activity_data_res.unwrap();
141        assert_eq!(activity_data.found, true);
142        assert_eq!(activity_data.active_in_days, Some(180));
143    }
144
145    #[test]
146    fn parse_api_usage() {
147        let api_usage: serde_json::Result<ApiUsage> = serde_json::from_str(API_USAGE_RESPONSE);
148        assert!(api_usage.is_ok());
149
150        let api_usage_obj = api_usage.unwrap();
151        let expected_start_date = NaiveDate::from_ymd_opt(2010, 1, 12).unwrap();
152        let expected_end_date = NaiveDate::from_ymd_opt(2030, 12, 1).unwrap();
153        assert_eq!(api_usage_obj.start_date, expected_start_date);
154        assert_eq!(api_usage_obj.end_date, expected_end_date);
155    }
156
157    #[test]
158    fn parse_find_mail_invalid_status() {
159        let find_mail: serde_json::Result<FindEmailResponse> = serde_json::from_str(MOCK_FIND_MAIL_INVALID);
160        assert!(find_mail.is_ok());
161
162        let find_mail_object = find_mail.unwrap();
163        assert_eq!(find_mail_object.email, "");
164        assert_eq!(find_mail_object.other_domain_formats.len(), 0);
165    }
166
167    #[test]
168    fn parse_find_mail_valid_status() {
169        let find_mail: serde_json::Result<FindEmailResponse> = serde_json::from_str(MOCK_FIND_MAIL_VALID);
170        assert!(find_mail.is_ok());
171
172        let find_mail_object = find_mail.unwrap();
173        assert_eq!(find_mail_object.email, "john.doe@example.com");
174        assert_eq!(find_mail_object.other_domain_formats.len(), 2);
175        assert_eq!(find_mail_object.other_domain_formats[0].confidence, "high");
176        assert_eq!(find_mail_object.other_domain_formats[1].confidence, "medium");
177    }
178
179
180}