zero_bounce/api/
validation.rs

1use std::collections::HashMap;
2
3use serde::Serialize;
4use serde_json::from_str;
5use serde_json::{Map as SerdeMap, Value};
6
7use crate::{ZeroBounce,  ZBResult};
8use crate::utility::{ENDPOINT_VALIDATE, ZBError, ENDPOINT_BATCH_VALIDATE, CONTENT_TYPE_JSON};
9use crate::utility::structures::validation::{ZBValidation, ZBBatchValidation};
10
11
12impl ZeroBounce {
13
14    pub fn validate_email_and_ip(&self, email: &str, ip_address: &str) -> ZBResult<ZBValidation> {
15        let mut query_args = HashMap::from([
16            ("email", email),
17        ]);
18
19        if !ip_address.is_empty() {
20            query_args.insert("ip_address", ip_address);
21        }
22
23        let response_content = self.generic_get_request(
24            self.url_provider.url_of(ENDPOINT_VALIDATE), query_args
25        )?;
26
27        let validation = from_str::<ZBValidation>(&response_content)?;
28        Ok(validation)
29    }
30
31    pub fn validate_email(&self, email: &str) -> ZBResult<ZBValidation> {
32        self.validate_email_and_ip(email, "")
33    }
34
35    // Represent a list of tuples (containing email and ip_address) into a
36    // serializable `serde_json::Value` that respects the expected structure
37    // of the batch validation endpoint.
38    //
39    // Said structure:
40    // ```json
41    // {
42    //     "api_key": {{apikey}},
43    //     "email_batch": [
44    //         {"email_address": "valid@example.com", "ip_address": "0.0.0.0"},
45    //         {"email_address": "invalid@example.com", "ip_address": "1.1.1.1"}
46    //     ]
47    // }
48    // ```
49    // After the value is built, serialize and return the resulted string.
50    fn batch_validate_prepare_body(&self, emails_and_ip_addresses: Vec<(String, String)>) -> ZBResult<String> {
51        let email_batch = emails_and_ip_addresses
52            .into_iter()
53            .map(|(email, ip_address)|
54                [
55                    ("email_address".to_string(), Value::String(email)),
56                    ("ip_address".to_string(), Value::String(ip_address)),
57                ]
58            )
59            .map(SerdeMap::<String, Value>::from_iter)
60            .map(Value::Object)
61            .collect::<Vec<Value>>();
62
63        let request_body_map = SerdeMap::from_iter([
64            ("api_key".to_string(), Value::String(self.api_key.clone())),
65            ("email_batch".to_string(), Value::Array(email_batch)),
66        ]);
67
68        // let request_body_object = Value::Object(request_body_map);
69        let mut serializer = serde_json::Serializer::new(Vec::new());
70        Value::Object(request_body_map)
71            .serialize(&mut serializer)
72            .map_err(ZBError::JsonError)?;
73
74        let final_string = String::from_utf8(serializer.into_inner())
75            .map_err(|error| ZBError::ExplicitError(error.to_string()))?;
76
77        Ok(final_string)
78    }
79
80    pub fn batch_validate(&self, emails_and_ip_addresses: Vec<(String, String)>) -> ZBResult<ZBBatchValidation> {
81        let body_content = self.batch_validate_prepare_body(emails_and_ip_addresses)?;
82        let url = self.url_provider.url_of(ENDPOINT_BATCH_VALIDATE);
83
84        let response = self.client.post(url)
85            .body(body_content)
86            .header("content-type", CONTENT_TYPE_JSON)
87            .send()?;
88
89        let response_ok = response.status().is_success();
90        let response_content = response.text()?;
91
92        // Debug: Print raw response to examine structure in debug mode
93        #[cfg(debug_assertions)]
94        {
95            eprintln!("Raw API response: {}", response_content);
96        }
97
98        if !response_ok {
99            return Err(ZBError::ExplicitError(response_content));
100        }
101
102        let validation = from_str::<ZBBatchValidation>(response_content.as_str())?;
103        Ok(validation)
104    }
105
106}
107
108#[cfg(test)]
109mod test {
110    use crate::ZeroBounce;
111
112    #[test]
113    fn test_serializing_example() {
114        let emails_and_ip_addresses = vec![
115            ("valid@example.com".to_string(), "123.123.123.123".to_string()),
116            ("invalid@example.com".to_string(), "".to_string()),
117        ];
118
119        let body_result = ZeroBounce::new("some_api_key")
120            .batch_validate_prepare_body(emails_and_ip_addresses);
121
122        assert!(body_result.is_ok())
123    }
124
125}