konduto/types/delivery/
details.rs

1use crate::types::delivery::delivery_company::DeliveryCompany;
2use crate::types::delivery::delivery_method::DeliveryMethod;
3use crate::types::shared::date_iso_8601::DateIso8601;
4use crate::types::validation_errors::ValidationError;
5use serde::{Deserialize, Serialize};
6
7/// Dados de entrega
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
9pub struct Delivery {
10    /// Empresa de entrega (max 100 chars)
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub delivery_company: Option<DeliveryCompany>,
13
14    /// Método de entrega (max 100 chars)
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub delivery_method: Option<DeliveryMethod>,
17
18    /// Data estimada de envio (formato ISO 8601: YYYY-MM-DD)
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub estimated_shipping_date: Option<DateIso8601>,
21
22    /// Data estimada de entrega (formato ISO 8601: YYYY-MM-DD)
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub estimated_delivery_date: Option<DateIso8601>,
25}
26
27impl Delivery {
28    /// Cria um builder para construir dados de entrega
29    pub fn builder() -> DeliveryBuilder {
30        DeliveryBuilder::default()
31    }
32}
33
34/// Builder para criar dados de entrega
35#[derive(Default, Debug)]
36pub struct DeliveryBuilder {
37    delivery: Delivery,
38}
39
40impl DeliveryBuilder {
41    /// Define a empresa de entrega
42    pub fn delivery_company(mut self, company: DeliveryCompany) -> Self {
43        self.delivery.delivery_company = Some(company);
44        self
45    }
46
47    /// Define a empresa de entrega a partir de uma string
48    pub fn delivery_company_str(
49        mut self,
50        company: impl Into<String>,
51    ) -> Result<Self, ValidationError> {
52        self.delivery.delivery_company = Some(DeliveryCompany::try_new(company)?);
53        Ok(self)
54    }
55
56    /// Define o método de entrega
57    pub fn delivery_method(mut self, method: DeliveryMethod) -> Self {
58        self.delivery.delivery_method = Some(method);
59        self
60    }
61
62    /// Define o método de entrega a partir de uma string
63    pub fn delivery_method_str(
64        mut self,
65        method: impl Into<String>,
66    ) -> Result<Self, ValidationError> {
67        self.delivery.delivery_method = Some(DeliveryMethod::try_new(method)?);
68        Ok(self)
69    }
70
71    /// Define a data estimada de envio
72    pub fn estimated_shipping_date(mut self, date: DateIso8601) -> Self {
73        self.delivery.estimated_shipping_date = Some(date);
74        self
75    }
76
77    /// Define a data estimada de envio a partir de uma string
78    pub fn estimated_shipping_date_str(
79        mut self,
80        date: impl Into<String>,
81    ) -> Result<Self, ValidationError> {
82        self.delivery.estimated_shipping_date = Some(DateIso8601::new(date)?);
83        Ok(self)
84    }
85
86    /// Define a data estimada de entrega
87    pub fn estimated_delivery_date(mut self, date: DateIso8601) -> Self {
88        self.delivery.estimated_delivery_date = Some(date);
89        self
90    }
91
92    /// Define a data estimada de entrega a partir de uma string
93    pub fn estimated_delivery_date_str(
94        mut self,
95        date: impl Into<String>,
96    ) -> Result<Self, ValidationError> {
97        self.delivery.estimated_delivery_date = Some(DateIso8601::new(date)?);
98        Ok(self)
99    }
100
101    /// Constrói os dados de entrega
102    pub fn build(self) -> Delivery {
103        self.delivery
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_delivery_builder_all_fields() {
113        let delivery = Delivery::builder()
114            .delivery_company_str("Correios")
115            .unwrap()
116            .delivery_method_str("SEDEX")
117            .unwrap()
118            .estimated_shipping_date_str("2025-12-01")
119            .unwrap()
120            .estimated_delivery_date_str("2025-12-05")
121            .unwrap()
122            .build();
123
124        assert!(delivery.delivery_company.is_some());
125        assert_eq!(delivery.delivery_company.unwrap().as_str(), "Correios");
126        assert!(delivery.delivery_method.is_some());
127        assert!(delivery.estimated_shipping_date.is_some());
128        assert!(delivery.estimated_delivery_date.is_some());
129    }
130
131    #[test]
132    fn test_delivery_builder_with_typed_values() {
133        let company = DeliveryCompany::try_new("FedEx").unwrap();
134        let method = DeliveryMethod::try_new("Express").unwrap();
135        let ship_date = DateIso8601::new("2025-11-20").unwrap();
136        let delivery_date = DateIso8601::new("2025-11-22").unwrap();
137
138        let delivery = Delivery::builder()
139            .delivery_company(company)
140            .delivery_method(method)
141            .estimated_shipping_date(ship_date)
142            .estimated_delivery_date(delivery_date)
143            .build();
144
145        assert!(delivery.delivery_company.is_some());
146        assert_eq!(
147            delivery.delivery_company.as_ref().unwrap().as_str(),
148            "FedEx"
149        );
150        assert_eq!(
151            delivery.delivery_method.as_ref().unwrap().as_str(),
152            "Express"
153        );
154    }
155
156    #[test]
157    fn test_delivery_builder_partial_fields() {
158        let delivery = Delivery::builder()
159            .delivery_company_str("DHL")
160            .unwrap()
161            .estimated_delivery_date_str("2025-12-10")
162            .unwrap()
163            .build();
164
165        assert!(delivery.delivery_company.is_some());
166        assert!(delivery.delivery_method.is_none());
167        assert!(delivery.estimated_shipping_date.is_none());
168        assert!(delivery.estimated_delivery_date.is_some());
169    }
170
171    #[test]
172    fn test_delivery_default() {
173        let delivery = Delivery::default();
174
175        assert!(delivery.delivery_company.is_none());
176        assert!(delivery.delivery_method.is_none());
177        assert!(delivery.estimated_shipping_date.is_none());
178        assert!(delivery.estimated_delivery_date.is_none());
179    }
180
181    #[test]
182    fn test_delivery_empty_builder() {
183        let delivery = Delivery::builder().build();
184
185        assert!(delivery.delivery_company.is_none());
186        assert!(delivery.delivery_method.is_none());
187        assert!(delivery.estimated_shipping_date.is_none());
188        assert!(delivery.estimated_delivery_date.is_none());
189    }
190
191    #[test]
192    fn test_delivery_company_validation() {
193        let result = Delivery::builder().delivery_company_str("").unwrap_err();
194
195        // Com nutype, o erro vem como Generic (mas a validação funciona)
196        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::EmptyField(_)));
197    }
198
199    #[test]
200    fn test_delivery_company_too_long() {
201        let long_company = "a".repeat(101);
202        let result = Delivery::builder()
203            .delivery_company_str(long_company)
204            .unwrap_err();
205
206        // Com nutype, o erro vem como Generic (mas a validação funciona)
207        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::TooLong { .. }));
208    }
209
210    #[test]
211    fn test_delivery_method_validation() {
212        let result = Delivery::builder().delivery_method_str("").unwrap_err();
213
214        // Com nutype, o erro vem como Generic (mas a validação funciona)
215        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::EmptyField(_)));
216    }
217
218    #[test]
219    fn test_delivery_method_too_long() {
220        let long_method = "a".repeat(101);
221        let result = Delivery::builder()
222            .delivery_method_str(long_method)
223            .unwrap_err();
224
225        // Com nutype, o erro vem como Generic (mas a validação funciona)
226        assert!(matches!(result, ValidationError::Generic(_) | ValidationError::TooLong { .. }));
227    }
228
229    #[test]
230    fn test_invalid_shipping_date_format() {
231        let result = Delivery::builder()
232            .estimated_shipping_date_str("2025/12/01")
233            .unwrap_err();
234
235        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
236    }
237
238    #[test]
239    fn test_invalid_delivery_date_format() {
240        let result = Delivery::builder()
241            .estimated_delivery_date_str("01-12-2025")
242            .unwrap_err();
243
244        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
245    }
246
247    #[test]
248    fn test_invalid_date_values() {
249        let result = Delivery::builder()
250            .estimated_shipping_date_str("2025-13-01")
251            .unwrap_err();
252
253        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
254    }
255
256    #[test]
257    fn test_delivery_serialization() {
258        let delivery = Delivery::builder()
259            .delivery_company_str("Correios")
260            .unwrap()
261            .delivery_method_str("PAC")
262            .unwrap()
263            .estimated_shipping_date_str("2025-11-25")
264            .unwrap()
265            .estimated_delivery_date_str("2025-11-30")
266            .unwrap()
267            .build();
268
269        let json = serde_json::to_string(&delivery).unwrap();
270
271        assert!(json.contains("Correios"));
272        assert!(json.contains("PAC"));
273        assert!(json.contains("2025-11-25"));
274        assert!(json.contains("2025-11-30"));
275    }
276
277    #[test]
278    fn test_delivery_deserialization() {
279        let json = r#"{
280            "delivery_company": "UPS",
281            "delivery_method": "Ground",
282            "estimated_shipping_date": "2025-12-01",
283            "estimated_delivery_date": "2025-12-05"
284        }"#;
285
286        let delivery: Delivery = serde_json::from_str(json).unwrap();
287
288        assert_eq!(delivery.delivery_company.unwrap().as_str(), "UPS");
289        assert_eq!(delivery.delivery_method.unwrap().as_str(), "Ground");
290        assert_eq!(
291            delivery.estimated_shipping_date.unwrap().as_str(),
292            "2025-12-01"
293        );
294        assert_eq!(
295            delivery.estimated_delivery_date.unwrap().as_str(),
296            "2025-12-05"
297        );
298    }
299
300    #[test]
301    fn test_delivery_deserialization_partial() {
302        let json = r#"{
303            "delivery_company": "DHL"
304        }"#;
305
306        let delivery: Delivery = serde_json::from_str(json).unwrap();
307
308        assert!(delivery.delivery_company.is_some());
309        assert!(delivery.delivery_method.is_none());
310        assert!(delivery.estimated_shipping_date.is_none());
311        assert!(delivery.estimated_delivery_date.is_none());
312    }
313
314    #[test]
315    fn test_delivery_serialization_skip_none() {
316        let delivery = Delivery::builder()
317            .delivery_company_str("FedEx")
318            .unwrap()
319            .build();
320
321        let json = serde_json::to_string(&delivery).unwrap();
322
323        assert!(json.contains("delivery_company"));
324        assert!(!json.contains("delivery_method"));
325        assert!(!json.contains("estimated_shipping_date"));
326        assert!(!json.contains("estimated_delivery_date"));
327    }
328
329    #[test]
330    fn test_delivery_roundtrip() {
331        let original = Delivery::builder()
332            .delivery_company_str("Correios")
333            .unwrap()
334            .delivery_method_str("SEDEX")
335            .unwrap()
336            .estimated_shipping_date_str("2025-11-20")
337            .unwrap()
338            .estimated_delivery_date_str("2025-11-25")
339            .unwrap()
340            .build();
341
342        let json = serde_json::to_string(&original).unwrap();
343        let deserialized: Delivery = serde_json::from_str(&json).unwrap();
344
345        assert_eq!(original, deserialized);
346    }
347
348    #[test]
349    fn test_delivery_clone() {
350        let delivery1 = Delivery::builder()
351            .delivery_company_str("DHL")
352            .unwrap()
353            .build();
354
355        let delivery2 = delivery1.clone();
356
357        assert_eq!(delivery1, delivery2);
358    }
359
360    #[test]
361    fn test_delivery_equality() {
362        let delivery1 = Delivery::builder()
363            .delivery_company_str("FedEx")
364            .unwrap()
365            .delivery_method_str("Express")
366            .unwrap()
367            .build();
368
369        let delivery2 = Delivery::builder()
370            .delivery_company_str("FedEx")
371            .unwrap()
372            .delivery_method_str("Express")
373            .unwrap()
374            .build();
375
376        assert_eq!(delivery1, delivery2);
377    }
378
379    #[test]
380    fn test_delivery_inequality() {
381        let delivery1 = Delivery::builder()
382            .delivery_company_str("FedEx")
383            .unwrap()
384            .build();
385
386        let delivery2 = Delivery::builder()
387            .delivery_company_str("UPS")
388            .unwrap()
389            .build();
390
391        assert_ne!(delivery1, delivery2);
392    }
393
394    #[test]
395    fn test_delivery_debug() {
396        let delivery = Delivery::builder()
397            .delivery_company_str("Correios")
398            .unwrap()
399            .build();
400
401        let debug_str = format!("{:?}", delivery);
402        assert!(debug_str.contains("Delivery"));
403    }
404
405    #[test]
406    fn test_common_brazilian_companies() {
407        let companies = vec!["Correios", "Jadlog", "Loggi", "Total Express"];
408
409        for company_name in companies {
410            let delivery = Delivery::builder()
411                .delivery_company_str(company_name)
412                .unwrap()
413                .build();
414
415            assert_eq!(delivery.delivery_company.unwrap().as_str(), company_name);
416        }
417    }
418
419    #[test]
420    fn test_common_delivery_methods() {
421        let methods = vec!["SEDEX", "PAC", "Express", "Ground", "Standard"];
422
423        for method_name in methods {
424            let delivery = Delivery::builder()
425                .delivery_method_str(method_name)
426                .unwrap()
427                .build();
428
429            assert_eq!(delivery.delivery_method.unwrap().as_str(), method_name);
430        }
431    }
432
433    #[test]
434    fn test_delivery_with_same_date() {
435        let delivery = Delivery::builder()
436            .estimated_shipping_date_str("2025-12-01")
437            .unwrap()
438            .estimated_delivery_date_str("2025-12-01")
439            .unwrap()
440            .build();
441
442        assert_eq!(
443            delivery.estimated_shipping_date.unwrap().as_str(),
444            delivery.estimated_delivery_date.unwrap().as_str()
445        );
446    }
447
448    #[test]
449    fn test_delivery_date_validation_edge_cases() {
450        // Último dia do mês
451        let delivery = Delivery::builder()
452            .estimated_shipping_date_str("2025-01-31")
453            .unwrap()
454            .build();
455        assert!(delivery.estimated_shipping_date.is_some());
456
457        // Fevereiro em ano não bissexto
458        let delivery = Delivery::builder()
459            .estimated_shipping_date_str("2025-02-28")
460            .unwrap()
461            .build();
462        assert!(delivery.estimated_shipping_date.is_some());
463
464        // Fevereiro em ano bissexto
465        let delivery = Delivery::builder()
466            .estimated_shipping_date_str("2024-02-29")
467            .unwrap()
468            .build();
469        assert!(delivery.estimated_shipping_date.is_some());
470    }
471
472    #[test]
473    fn test_delivery_invalid_february_29_non_leap_year() {
474        let result = Delivery::builder()
475            .estimated_shipping_date_str("2025-02-29")
476            .unwrap_err();
477
478        assert!(matches!(result, ValidationError::InvalidFormat { .. }));
479    }
480
481    #[test]
482    fn test_delivery_company_with_special_chars() {
483        let delivery = Delivery::builder()
484            .delivery_company_str("DHL Express (Brasil)")
485            .unwrap()
486            .build();
487
488        assert_eq!(
489            delivery.delivery_company.unwrap().as_str(),
490            "DHL Express (Brasil)"
491        );
492    }
493
494    #[test]
495    fn test_delivery_method_with_special_chars() {
496        let delivery = Delivery::builder()
497            .delivery_method_str("Next-Day Air")
498            .unwrap()
499            .build();
500
501        assert_eq!(delivery.delivery_method.unwrap().as_str(), "Next-Day Air");
502    }
503}