konduto/types/address/
mod.rs

1use serde::{Deserialize, Serialize};
2mod address_line;
3mod address_name;
4mod city;
5mod country_code;
6mod state;
7mod zip_code;
8
9pub use address_line::AddressLine;
10pub use address_name::AddressName;
11pub use city::City;
12pub use country_code::{Country, CountryCode};
13pub use state::State;
14pub use zip_code::ZipCode;
15
16/// Dados de endereço com validação tipo-segura
17#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
18pub struct Address {
19    /// Nome completo do destinatário (max 100 chars)
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub name: Option<AddressName>,
22
23    /// Endereço principal (max 255 chars)
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub address1: Option<AddressLine>,
26
27    /// Complemento adicional (max 255 chars)
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub address2: Option<AddressLine>,
30
31    /// Cidade (max 100 chars)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub city: Option<City>,
34
35    /// Estado/Província (max 100 chars)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub state: Option<State>,
38
39    /// CEP (max 100 chars)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub zip: Option<ZipCode>,
42
43    /// País - código ISO 3166-2 (exatamente 2 chars)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub country: Option<Country>,
46}
47
48impl Address {
49    /// Cria um builder para construir um endereço
50    pub fn builder() -> AddressBuilder {
51        AddressBuilder::default()
52    }
53}
54
55/// Builder para criar endereços
56#[derive(Default)]
57pub struct AddressBuilder {
58    address: Address,
59}
60
61impl AddressBuilder {
62    /// Define o nome do destinatário a partir de uma string
63    pub fn name(
64        mut self,
65        name: impl Into<String>,
66    ) -> Result<Self, crate::types::validation_errors::ValidationError> {
67        self.address.name = Some(AddressName::try_new(name)?);
68        Ok(self)
69    }
70
71    /// Define o endereço principal a partir de uma string
72    pub fn address1(
73        mut self,
74        address: impl Into<String>,
75    ) -> Result<Self, crate::types::validation_errors::ValidationError> {
76        self.address.address1 = Some(AddressLine::try_new(address)?);
77        Ok(self)
78    }
79
80    /// Define o complemento a partir de uma string
81    pub fn address2(
82        mut self,
83        address: impl Into<String>,
84    ) -> Result<Self, crate::types::validation_errors::ValidationError> {
85        self.address.address2 = Some(AddressLine::try_new(address)?);
86        Ok(self)
87    }
88
89    /// Define a cidade a partir de uma string
90    pub fn city(
91        mut self,
92        city: impl Into<String>,
93    ) -> Result<Self, crate::types::validation_errors::ValidationError> {
94        self.address.city = Some(City::try_new(city)?);
95        Ok(self)
96    }
97
98    /// Define o estado a partir de uma string
99    pub fn state(
100        mut self,
101        state: impl Into<String>,
102    ) -> Result<Self, crate::types::validation_errors::ValidationError> {
103        self.address.state = Some(State::try_new(state)?);
104        Ok(self)
105    }
106
107    /// Define o CEP a partir de uma string
108    pub fn zip(
109        mut self,
110        zip: impl Into<String>,
111    ) -> Result<Self, crate::types::validation_errors::ValidationError> {
112        self.address.zip = Some(ZipCode::try_new(zip)?);
113        Ok(self)
114    }
115
116    /// Define o país a partir de uma string
117    pub fn country(mut self, country: Country) -> Self {
118        self.address.country = Some(country);
119        self
120    }
121
122    /// Constrói o endereço
123    pub fn build(self) -> Address {
124        self.address
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn test_address_builder() -> Result<(), Box<dyn std::error::Error>> {
134        let address = Address::builder()
135            .name("John Doe")?
136            .address1("123 Main St")?
137            .city("São Paulo")?
138            .state("SP")?
139            .zip("01234-567")?
140            .country(Country::BR)
141            .build();
142
143        assert!(address.name.is_some());
144        assert!(address.address1.is_some());
145        assert!(address.city.is_some());
146        assert_eq!(address.country, Some(Country::BR));
147        Ok(())
148    }
149
150    #[test]
151    fn test_address_serialization() -> Result<(), Box<dyn std::error::Error>> {
152        let address = Address::builder()
153            .name("Jane Doe")?
154            .address1("456 Oak Ave")?
155            .city("Rio de Janeiro")?
156            .state("RJ")?
157            .zip("20000-000")?
158            .country(Country::BR)
159            .build();
160
161        let json = serde_json::to_string(&address).unwrap();
162        assert!(json.contains("Jane Doe"));
163        assert!(json.contains("Rio de Janeiro"));
164        Ok(())
165    }
166
167    #[test]
168    fn test_address_default() {
169        let address = Address::default();
170        assert!(address.name.is_none());
171        assert!(address.address1.is_none());
172    }
173
174    #[test]
175    fn test_invalid_country_code() {
176        let result = Country::from_code("USA"); // 3 chars instead of 2
177        assert!(result.is_err());
178    }
179
180    #[test]
181    fn test_name_too_long() {
182        let long_name = "a".repeat(101);
183        let result = AddressName::try_new(long_name);
184        assert!(result.is_err());
185    }
186
187    #[test]
188    fn test_address_line_too_long() {
189        let long_address = "a".repeat(256);
190        let result = AddressLine::try_new(long_address);
191        assert!(result.is_err());
192    }
193}