brasilapi_client/definitions/
cep.rs

1use crate::{client::*, constants::cep::{SVC_V1_URL, SVC_V2_URL}, errors::*, request::*, commons::EmptyOption};
2use serde::{Deserialize, Serialize};
3
4/**
5The Desired CEP Search Version
6*/
7pub enum EnumCepRequestVersion {
8    /// V1 for common data, without GeoLocalization
9    V1,
10    /// V2 for common data + GeoLocalization
11    V2
12}
13
14#[derive(Serialize, Deserialize, Debug)]
15#[serde(rename_all = "camelCase")]
16/// The coordinates in Latitude & Longitude for this address
17pub struct Coordinates {
18    /// The Latitude
19    pub latitude: String,
20    /// The Longitude
21    pub longitude: String
22}
23
24impl PartialEq for Coordinates {
25    fn eq(&self, other: &Self) -> bool {
26        self.latitude == other.latitude && self.longitude == other.longitude
27    }
28}
29
30#[derive(Serialize, Deserialize, Debug)]
31#[serde(rename_all = "camelCase")]
32/// GeoLocation info
33pub struct Location
34{
35    /// The GeoCoordinates
36    pub coordinates: EmptyOption<Coordinates>
37}
38
39impl Default for Location {
40    fn default() -> Self {
41        Location { coordinates: EmptyOption::Some(Coordinates { latitude: String::new(), longitude: String::new() }) }
42    }
43}
44
45impl PartialEq for Location {
46    fn eq(&self, other: &Self) -> bool {
47        self.coordinates.as_option() == other.coordinates.as_option()
48    }
49}
50
51#[derive(Serialize, Deserialize, Debug)]
52#[serde(rename_all = "camelCase")]
53/// The Zipcode data struct
54pub struct CepResponseData {
55    /// The zipcode itself
56    pub cep: String,
57    /// The State name
58    pub state: String,
59    /// The City name
60    pub city: String,
61    /// The Neighborhood name
62    pub neighborhood: String,
63    /// The Street name
64    pub street: String,
65    /// Which service returned this
66    pub service: String,
67    /// The Geolocation data, only filled on V2
68    #[serde(default)]
69    pub location: Location
70}
71
72impl PartialEq for CepResponseData {
73    fn eq(&self, other: &Self) -> bool {
74        self.cep == other.cep 
75        && self.state == other.state 
76        && self.city == other.city 
77        && self.neighborhood == other.neighborhood 
78        && self.street == other.street
79        && self.location == other.location
80    }
81}
82
83impl BrasilApiClient {
84    pub async fn get_cep(&self, cep: &str, cep_version: Option<EnumCepRequestVersion>) -> Result<CepResponseData, Error> {
85        lazy_static! {
86            static ref RE: regex::Regex = regex::Regex::new(r"[^0-9]").unwrap();
87        }
88        let cepver = cep_version.unwrap_or(EnumCepRequestVersion::V1);
89        let url = match cepver {
90            EnumCepRequestVersion::V2 => SVC_V2_URL,
91            _ => SVC_V1_URL     
92        };
93        
94        let temp_zipcode = RE.replace_all(cep, "");
95        if temp_zipcode.is_empty() || temp_zipcode.len() > 8 {
96            return Err(Error::InvalidInputLenError
97                {
98                    name: "cep".to_string(),
99                    min: 8, 
100                    max: 8
101                })
102        }
103        
104        Ok(get::<(), CepResponseData>(
105            &format!("{}/{}/{}", self.base_url, url, temp_zipcode)
106        ).await?)
107    }
108}
109
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use crate::client::tests::*;
115    use futures_await_test::async_test;
116
117    #[async_test]
118    async fn testc_invalid_input_minlen_none_ver() {
119        let resp = cli().get_cep("09777", None)
120        .await;
121
122        assert!(resp.is_err());
123    }
124
125    #[async_test]
126    async fn test_invalid_input_empty_none_ver() {
127        let resp = cli().get_cep("", None)
128        .await;
129
130        assert!(resp.is_err());
131    }
132
133    #[async_test]
134    async fn test_invalid_cep() {
135        let resp = cli().get_cep("12345678", None)
136        .await;
137
138        assert!(resp.is_err());
139    }
140
141    #[async_test]
142    async fn test_valid_none_ver() {
143        let resp = cli().get_cep("01402-000", None).await;
144        assert!(resp.is_ok());
145
146        let expected_text = r#"{"cep":"01402000","state":"SP","city":"São Paulo","neighborhood":"Jardim Paulista","street":"Avenida Brigadeiro Luís Antônio","service":"viacep"}"#;
147        let mut expected_json = serde_json::from_str::<CepResponseData>(expected_text).unwrap();
148        expected_json.service = "".into();
149
150        let mut from_svc = resp.unwrap();
151        from_svc.service = "".into();
152
153        assert_eq!(from_svc, expected_json);
154    }
155
156    #[async_test]
157    async fn test_valid_v1_same_as_none() {
158        let resp_v1 = cli().get_cep("01402-000", Some(EnumCepRequestVersion::V1)).await;
159        let resp_none = cli().get_cep("01402-000", None).await;
160        assert!(resp_v1.is_ok());
161        assert!(resp_none.is_ok());
162
163        let expected_text = r#"{"cep":"01402000","state":"SP","city":"São Paulo","neighborhood":"Jardim Paulista","street":"Avenida Brigadeiro Luís Antônio","service":"viacep"}"#;
164        let mut expected_json = serde_json::from_str::<CepResponseData>(expected_text).unwrap();
165        expected_json.service = "".into();
166
167        let mut from_svc = resp_v1.unwrap();
168        from_svc.service = "".into();
169
170        assert_eq!(from_svc, expected_json);
171
172        let mut from_svc_none = resp_none.unwrap();
173        from_svc_none.service = "".into();
174
175        assert_eq!(from_svc, from_svc_none);
176    }
177
178    #[async_test]
179    async fn test_valid_v2() {
180        let resp = cli().get_cep("01402-000", Some(EnumCepRequestVersion::V2)).await;
181        assert!(resp.is_ok());
182
183        let expected_text = r#"{"cep":"01402000","state":"SP","city":"São Paulo","neighborhood":"Jardim Paulista","street":"Avenida Brigadeiro Luís Antônio","service":"viacep","location":{"type":"Point","coordinates":{"longitude":"-46.6573802","latitude":"-23.57555"}}}"#;
184        //let expected_text = r#"{"cep":"01402000","state":"SP","city":"São Paulo","neighborhood":"Jardim Paulista","street":"Avenida Brigadeiro Luís Antônio","service":"viacep","location":{"type":"Point","coordinates":{"longitude":"-46.6367822","latitude":"-23.5507017"}}}"#;
185        let mut expected_json = serde_json::from_str::<CepResponseData>(expected_text).unwrap();
186        expected_json.service = "".into();
187
188        let mut from_svc = resp.unwrap();
189        from_svc.service = "".into();
190
191        assert_eq!(from_svc, expected_json);
192    }
193
194    #[async_test]
195    async fn test_valid_v2_not_equals_v1() {
196        let resp_v2 = cli().get_cep("01402-000", Some(EnumCepRequestVersion::V2)).await;
197        let resp_none = cli().get_cep("01402-000", None).await;
198        assert!(resp_v2.is_ok());
199        assert!(resp_none.is_ok());
200
201        let expected_text = r#"{"cep":"01402000","state":"SP","city":"São Paulo","neighborhood":"Jardim Paulista","street":"Avenida Brigadeiro Luís Antônio","service":"viacep","location":{"type":"Point","coordinates":{"longitude":"-46.6573802","latitude":"-23.57555"}}}"#;
202        let mut expected_json = serde_json::from_str::<CepResponseData>(expected_text).unwrap();
203        expected_json.service = "".into();
204
205        let mut from_svc = resp_v2.unwrap();
206        from_svc.service = "".into();
207
208        assert_eq!(from_svc, expected_json);
209
210        let mut from_svc_none = resp_none.unwrap();
211        from_svc_none.service = "".into();
212
213        assert_ne!(from_svc, from_svc_none);
214    }
215}