postal_code/
postal_code.rs1crate::ix!();
2
3#[derive(Builder,Debug,Hash,Clone,PartialEq,Eq,Serialize,Deserialize,Getters,Ord,PartialOrd)]
5#[builder(build_fn(error = "PostalCodeConstructionError",validate = "Self::validate"))]
6pub struct PostalCode {
7
8 #[getset(get = "pub")]
10 country: Country,
11
12 #[getset(get = "pub")]
14 code: String,
15}
16
17impl PostalCode {
18
19 pub fn new(country: Country, code: &str) -> Result<Self,PostalCodeConstructionError> {
20 PostalCodeBuilder::default()
21 .country(country)
22 .code(code.to_string())
23 .build()
24 }
25}
26
27impl PostalCodeBuilder {
28 pub fn validate(&self) -> Result<(), PostalCodeConstructionError> {
29 if self.country.is_none() || self.code.is_none() {
30 return Err(PostalCodeConstructionError::InvalidFormat {
31 attempted_code: "<unset>".to_string(),
32 attempted_country: None,
33 });
34 }
35 let country = self.country.as_ref().unwrap();
36 let code = self.code.as_ref().unwrap();
37
38 if let Some(validator) = country.get_postal_code_validator() {
39 if validator.validate(code) {
40 Ok(())
41 } else {
42 Err(PostalCodeConstructionError::InvalidFormat {
43 attempted_code: code.clone(),
44 attempted_country: Some(country.clone()),
45 })
46 }
47 } else {
48 Err(PostalCodeConstructionError::UnsupportedCountry {
49 attempted_country: country.clone(),
50 })
51 }
52 }
53}
54
55#[cfg(test)]
56mod postal_code_tests {
57 use super::*;
58 use country::Country;
59 use rand::Rng; #[test]
62 fn test_us_valid() {
63 let pc = PostalCode::new(Country::USA, "12345");
64 assert!(pc.is_ok());
65 assert_eq!(pc.unwrap().code(), "12345");
66 }
67
68 #[test]
69 fn test_us_valid_zip_plus4() {
70 let pc = PostalCode::new(Country::USA, "12345-6789");
71 assert!(pc.is_ok());
72 }
73
74 #[test]
75 fn test_us_invalid_alphabetic() {
76 let pc = PostalCode::new(Country::USA, "ABCDE");
77 assert!(pc.is_err());
78 if let Err(PostalCodeConstructionError::InvalidFormat { attempted_code, attempted_country }) = pc {
79 assert_eq!(attempted_code, "ABCDE");
80 assert_eq!(attempted_country, Some(Country::USA));
81 } else {
82 panic!("Unexpected error type");
83 }
84 }
85
86 #[test]
87 fn test_us_invalid_length() {
88 let pc = PostalCode::new(Country::USA, "1234");
89 assert!(pc.is_err());
90 }
91
92 #[test]
93 fn test_ca_valid() {
94 let pc = PostalCode::new(Country::Canada, "K1A0B1");
95 assert!(pc.is_ok());
96 }
97
98 #[test]
99 fn test_ca_valid_with_space() {
100 let pc = PostalCode::new(Country::Canada, "K1A 0B1");
102 assert!(pc.is_ok());
103 }
104
105 #[test]
106 fn test_ca_invalid() {
107 let pc = PostalCode::new(Country::Canada, "123456");
108 assert!(pc.is_err());
109 }
110
111 #[test]
112 fn test_ca_invalid_non_alphanumeric() {
113 let pc = PostalCode::new(Country::Canada, "K1A!0B1");
115 assert!(pc.is_err());
116 }
117
118 #[test]
119 fn test_uk_valid() {
120 let pc = PostalCode::new(Country::UnitedKingdom, "SW1A 1AA");
122 assert!(pc.is_ok());
123 }
124
125 #[test]
126 fn test_uk_invalid_no_space() {
127 let pc = PostalCode::new(Country::UnitedKingdom, "SW1A1AA");
129 assert!(pc.is_err());
130 }
131
132 #[test]
133 fn test_uk_invalid_too_long() {
134 let pc = PostalCode::new(Country::UnitedKingdom, "SW1A 1AAA");
135 assert!(pc.is_err());
136 }
137
138 #[test]
139 fn test_fr_valid() {
140 let pc = PostalCode::new(Country::France, "75001");
141 assert!(pc.is_ok());
142 }
143
144 #[test]
145 fn test_fr_invalid_short() {
146 let pc = PostalCode::new(Country::France, "7500");
147 assert!(pc.is_err());
148 }
149
150 #[test]
151 fn test_fr_invalid_alpha() {
152 let pc = PostalCode::new(Country::France, "75A01");
153 assert!(pc.is_err());
154 }
155
156 #[test]
157 fn test_de_valid() {
158 let pc = PostalCode::new(Country::Germany, "10115");
159 assert!(pc.is_ok());
160 }
161
162 #[test]
163 fn test_de_invalid_short() {
164 let pc = PostalCode::new(Country::Germany, "101");
165 assert!(pc.is_err());
166 }
167
168 #[test]
169 fn test_de_invalid_alpha() {
170 let pc = PostalCode::new(Country::Germany, "10A15");
171 assert!(pc.is_err());
172 }
173
174 #[test]
175 fn test_it_valid() {
176 let pc = PostalCode::new(Country::Italy, "00144");
177 assert!(pc.is_ok());
178 }
179
180 #[test]
181 fn test_it_invalid_short() {
182 let pc = PostalCode::new(Country::Italy, "0144");
183 assert!(pc.is_err());
184 }
185
186 #[test]
187 fn test_it_invalid_alpha() {
188 let pc = PostalCode::new(Country::Italy, "00A44");
189 assert!(pc.is_err());
190 }
191
192 #[test]
193 fn test_unsupported_country() {
194 let pc = PostalCode::new(Country::Uzbekistan, "12345");
195 assert!(pc.is_err());
196 if let Err(PostalCodeConstructionError::UnsupportedCountry { attempted_country }) = pc {
197 assert_eq!(attempted_country, Country::Uzbekistan);
198 } else {
199 panic!("Expected UnsupportedCountry error");
200 }
201 }
202
203 #[test]
204 fn test_missing_fields_via_builder() {
205 let pc = PostalCodeBuilder::default().build();
207 assert!(pc.is_err());
208 if let Err(PostalCodeConstructionError::InvalidFormat { attempted_code, attempted_country }) = pc {
209 assert_eq!(attempted_code, "<unset>");
210 assert_eq!(attempted_country, None);
211 } else {
212 panic!("Expected InvalidFormat error due to missing fields");
213 }
214 }
215
216 #[test]
217 fn test_multiple_random_us_codes() {
218 let mut rng = rand::thread_rng();
221 for _ in 0..10 {
222 let base: u32 = rng.gen_range(0..100000);
223 let code = format!("{:05}", base);
224 let pc = PostalCode::new(Country::USA, &code);
225 assert!(pc.is_ok());
226 }
227 for _ in 0..5 {
229 let code = format!("{}ABCD", rng.gen_range(0..10000));
230 let pc = PostalCode::new(Country::USA, &code);
231 assert!(pc.is_err());
232 }
233 }
234
235 #[test]
236 fn test_space_and_hyphen_tolerance_in_ca_codes() {
237 let pc = PostalCode::new(Country::Canada, "K1A-0B1");
239 assert!(pc.is_err());
240 }
241
242 #[test]
243 fn test_uk_gir_special_case() {
244 let pc = PostalCode::new(Country::UnitedKingdom, "GIR 0AA");
246 assert!(pc.is_ok());
247 }
248}