1use super::*;
2
3pub const CONTROL_SECTION_SIZE: u8 = 4;
4pub const ORDINAL_SECTION_SIZE: u8 = 14;
5pub const DAY_SECTION_SIZE: u8 = 7;
6pub const MONTH_SECTION_SIZE: u8 = 5;
7pub const YEAR_SECTION_SIZE: u8 = 7;
8
9pub const CONTROL_SECTION_SHIFT: u8 = 0;
10pub const ORDINAL_SECTION_SHIFT: u8 = CONTROL_SECTION_SIZE + 5;
11pub const DAY_SECTION_SHIFT: u8 = ORDINAL_SECTION_SHIFT + ORDINAL_SECTION_SIZE + 5;
12pub const MONTH_SECTION_SHIFT: u8 = DAY_SECTION_SHIFT + DAY_SECTION_SIZE + 5;
13pub const YEAR_SECTION_SHIFT: u8 = MONTH_SECTION_SHIFT + MONTH_SECTION_SIZE + 5;
14
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub struct Pesel(u64);
27
28impl_try_from_str_for_pesel!(Pesel);
29
30#[cfg(feature = "serde")]
31impl serde::Serialize for Pesel {
32 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
33 where
34 S: serde::Serializer,
35 {
36 serializer.serialize_u64(u64::from(self))
37 }
38}
39
40#[cfg(feature = "serde")]
41impl_pesel_deserializer!(Pesel);
42
43impl From<Pesel> for u64 {
44 fn from(value: Pesel) -> Self {
45 (&value).into()
46 }
47}
48
49impl From<&Pesel> for u64 {
50 fn from(value: &Pesel) -> Self {
51 value.control_section() as u64
52 + (value.ordinal_section() as u64) * 10u64.pow(1)
53 + (value.day_section() as u64) * 10u64.pow(5)
54 + (value.month_section() as u64) * 10u64.pow(7)
55 + (value.year_section() as u64) * 10u64.pow(9)
56 }
57}
58
59impl TryFrom<u64> for Pesel {
60 type Error = ValidationError;
61
62 fn try_from(value: u64) -> Result<Self, Self::Error> {
63 validate(value)?;
64 let pesel = Pesel(
65 ((crate::day_section(value) as u64) << DAY_SECTION_SHIFT as u64)
66 + ((crate::month_section(value) as u64) << MONTH_SECTION_SHIFT as u64)
67 + ((crate::year_section(value) as u64) << YEAR_SECTION_SHIFT as u64)
68 + ((crate::ordinal_section(value) as u64) << ORDINAL_SECTION_SHIFT as u64)
69 + ((crate::control_section(value) as u64) << CONTROL_SECTION_SHIFT as u64),
70 );
71 Ok(pesel)
72 }
73}
74
75impl From<crate::human_redable::Pesel> for Pesel {
76 fn from(value: crate::human_redable::Pesel) -> Self {
77 Self(
78 ((value.day_section() as u64) << DAY_SECTION_SHIFT as u64)
79 + ((value.month_section() as u64) << MONTH_SECTION_SHIFT as u64)
80 + ((value.year_section() as u64) << YEAR_SECTION_SHIFT as u64)
81 + ((value.ordinal_section() as u64) << ORDINAL_SECTION_SHIFT as u64)
82 + ((value.control_section() as u64) << CONTROL_SECTION_SHIFT as u64),
83 )
84 }
85}
86
87impl PeselTrait for Pesel {
88 fn day_section(&self) -> u8 {
89 (self.0 >> DAY_SECTION_SHIFT & (2u64.pow(DAY_SECTION_SIZE as u32) - 1)) as u8
90 }
91
92 fn month_section(&self) -> u8 {
93 (self.0 >> MONTH_SECTION_SHIFT & (2u64.pow(MONTH_SECTION_SIZE as u32) - 1)) as u8
94 }
95
96 fn year_section(&self) -> u8 {
97 (self.0 >> YEAR_SECTION_SHIFT & (2u64.pow(YEAR_SECTION_SIZE as u32) - 1)) as u8
98 }
99
100 fn ordinal_section(&self) -> u16 {
101 (self.0 >> ORDINAL_SECTION_SHIFT & (2u64.pow(ORDINAL_SECTION_SIZE as u32) - 1)) as u16
102 }
103
104 fn control_section(&self) -> u8 {
105 (self.0 >> CONTROL_SECTION_SHIFT & (2u64.pow(CONTROL_SECTION_SIZE as u32) - 1)) as u8
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use std::sync::LazyLock;
113
114 static PESEL1: LazyLock<Pesel> = LazyLock::new(|| Pesel::try_from(02290486168).unwrap());
115 static PESEL2: LazyLock<Pesel> = LazyLock::new(|| Pesel::try_from(01302534699).unwrap());
116 static PESEL3: LazyLock<Pesel> = LazyLock::new(|| Pesel::try_from(00010128545).unwrap());
117 static PESEL4: LazyLock<Pesel> = LazyLock::new(|| Pesel::try_from(98250993285).unwrap());
118 static PESEL5: LazyLock<Pesel> = LazyLock::new(|| Pesel::try_from(60032417874).unwrap());
119
120 #[test]
121 fn day_section() {
122 assert_eq!(PESEL1.day_section(), 04);
123 assert_eq!(PESEL2.day_section(), 25);
124 assert_eq!(PESEL3.day_section(), 01);
125 assert_eq!(PESEL4.day_section(), 09);
126 assert_eq!(PESEL5.day_section(), 24);
127 }
128
129 #[test]
130 fn month_section() {
131 assert_eq!(PESEL1.month_section(), 29);
132 assert_eq!(PESEL2.month_section(), 30);
133 assert_eq!(PESEL3.month_section(), 01);
134 assert_eq!(PESEL4.month_section(), 25);
135 assert_eq!(PESEL5.month_section(), 03);
136 }
137
138 #[test]
139 fn year_section() {
140 assert_eq!(PESEL1.year_section(), 02);
141 assert_eq!(PESEL2.year_section(), 01);
142 assert_eq!(PESEL3.year_section(), 00);
143 assert_eq!(PESEL4.year_section(), 98);
144 assert_eq!(PESEL5.year_section(), 60);
145 }
146
147 #[test]
148 fn ordinal_section() {
149 assert_eq!(PESEL1.ordinal_section(), 8616);
150 assert_eq!(PESEL2.ordinal_section(), 3469);
151 assert_eq!(PESEL3.ordinal_section(), 2854);
152 assert_eq!(PESEL4.ordinal_section(), 9328);
153 assert_eq!(PESEL5.ordinal_section(), 1787);
154 }
155
156 #[test]
157 fn control_section() {
158 assert_eq!(PESEL1.control_section(), 8);
159 assert_eq!(PESEL2.control_section(), 9);
160 assert_eq!(PESEL3.control_section(), 5);
161 assert_eq!(PESEL4.control_section(), 5);
162 assert_eq!(PESEL5.control_section(), 4);
163 }
164
165 #[test]
166 fn day() {
167 assert_eq!(PESEL1.day(), 04);
168 assert_eq!(PESEL2.day(), 25);
169 assert_eq!(PESEL3.day(), 01);
170 assert_eq!(PESEL4.day(), 09);
171 assert_eq!(PESEL5.day(), 24);
172 }
173
174 #[test]
175 fn month() {
176 assert_eq!(PESEL1.month(), 09);
177 assert_eq!(PESEL2.month(), 10);
178 assert_eq!(PESEL3.month(), 01);
179 assert_eq!(PESEL4.month(), 05);
180 assert_eq!(PESEL5.month(), 03);
181 }
182
183 #[test]
184 fn year() {
185 assert_eq!(PESEL1.year(), 2002);
186 assert_eq!(PESEL2.year(), 2001);
187 assert_eq!(PESEL3.year(), 1900);
188 assert_eq!(PESEL4.year(), 2098);
189 assert_eq!(PESEL5.year(), 1960);
190 }
191
192 #[test]
193 fn date_of_birth() {
194 assert_eq!(
195 PESEL1.date_of_birth(),
196 NaiveDate::from_ymd_opt(2002, 09, 04).unwrap()
197 );
198 assert_eq!(
199 PESEL2.date_of_birth(),
200 NaiveDate::from_ymd_opt(2001, 10, 25).unwrap()
201 );
202 assert_eq!(
203 PESEL3.date_of_birth(),
204 NaiveDate::from_ymd_opt(1900, 01, 01).unwrap()
205 );
206 assert_eq!(
207 PESEL4.date_of_birth(),
208 NaiveDate::from_ymd_opt(2098, 05, 09).unwrap()
209 );
210 assert_eq!(
211 PESEL5.date_of_birth(),
212 NaiveDate::from_ymd_opt(1960, 03, 24).unwrap()
213 );
214 }
215
216 #[test]
217 fn gender() {
218 assert_eq!(PESEL1.gender(), Gender::Female);
219 assert_eq!(PESEL2.gender(), Gender::Male);
220 assert_eq!(PESEL3.gender(), Gender::Female);
221 assert_eq!(PESEL4.gender(), Gender::Female);
222 assert_eq!(PESEL5.gender(), Gender::Male);
223 }
224
225 #[test]
226 fn invalid_pesels() {
227 assert_eq!(Pesel::try_from(4355), Err(ValidationError::TooShort(4)));
228 assert_eq!(
229 Pesel::try_from(435585930294485),
230 Err(ValidationError::TooLong(15))
231 );
232 assert_eq!(
233 Pesel::try_from(99990486167),
234 Err(ValidationError::BirthDate)
235 );
236 assert_eq!(
237 Pesel::try_from(02290486167),
238 Err(ValidationError::ControlDigit)
239 );
240 }
241
242 #[test]
243 fn try_from_strings() {
244 assert_eq!(
245 PESEL1.to_owned(),
246 Pesel::try_from(&String::from("02290486168")).unwrap()
247 );
248 assert_eq!(
249 PESEL2.to_owned(),
250 Pesel::try_from(&String::from("01302534699")).unwrap()
251 );
252 assert_eq!(
253 PESEL3.to_owned(),
254 Pesel::try_from(&String::from("00010128545")).unwrap()
255 );
256 assert_eq!(
257 PESEL4.to_owned(),
258 Pesel::try_from(&String::from("98250993285")).unwrap()
259 );
260 assert_eq!(
261 PESEL5.to_owned(),
262 Pesel::try_from(&String::from("60032417874")).unwrap()
263 );
264 }
265
266 #[test]
267 #[cfg(feature = "serde")]
268 fn deserialize() {
269 use serde_json::{from_value, json};
270
271 assert_eq!(
272 PESEL1.to_owned(),
273 from_value::<Pesel>(json!("02290486168")).expect("Valid PESEL")
274 );
275 assert_eq!(
276 PESEL1.to_owned(),
277 from_value::<Pesel>(json!(02290486168u64)).expect("Valid PESEL")
278 );
279
280 assert_eq!(
281 PESEL2.to_owned(),
282 from_value::<Pesel>(json!("01302534699")).expect("Valid PESEL")
283 );
284 assert_eq!(
285 PESEL2.to_owned(),
286 from_value::<Pesel>(json!(01302534699u64)).expect("Valid PESEL")
287 );
288
289 assert_eq!(
290 PESEL3.to_owned(),
291 from_value::<Pesel>(json!("00010128545")).expect("Valid PESEL")
292 );
293 assert_eq!(
294 PESEL3.to_owned(),
295 from_value::<Pesel>(json!(00010128545u64)).expect("Valid PESEL")
296 );
297
298 assert_eq!(
299 PESEL4.to_owned(),
300 from_value::<Pesel>(json!("98250993285")).expect("Valid PESEL")
301 );
302 assert_eq!(
303 PESEL4.to_owned(),
304 from_value::<Pesel>(json!(98250993285u64)).expect("Valid PESEL")
305 );
306
307 assert_eq!(
308 PESEL5.to_owned(),
309 from_value::<Pesel>(json!("60032417874")).expect("Valid PESEL")
310 );
311 assert_eq!(
312 PESEL5.to_owned(),
313 from_value::<Pesel>(json!(60032417874u64)).expect("Valid PESEL")
314 );
315 }
316
317 #[test]
318 #[cfg(feature = "serde")]
319 fn invalid_deserialize() {
320 use serde_json::{from_value, json};
321
322 from_value::<Pesel>(json!("60036817874")).expect_err("Invalid PESEL");
323 from_value::<Pesel>(json!(60036817874u64)).expect_err("Invalid PESEL");
324
325 from_value::<Pesel>(json!("00006817874")).expect_err("Invalid PESEL");
326 from_value::<Pesel>(json!(00006817874u64)).expect_err("Invalid PESEL");
327
328 from_value::<Pesel>(json!("02290486167")).expect_err("Invalid PESEL");
329 from_value::<Pesel>(json!(02290486167u64)).expect_err("Invalid PESEL");
330 }
331
332 #[test]
333 #[cfg(feature = "serde")]
334 fn serialize() {
335 use serde_json::{json, to_value};
336
337 assert_eq!(to_value(PESEL1.to_owned()).unwrap(), json!(02290486168u64));
338 assert_eq!(to_value(PESEL2.to_owned()).unwrap(), json!(01302534699u64));
339 assert_eq!(to_value(PESEL3.to_owned()).unwrap(), json!(00010128545u64));
340 assert_eq!(to_value(PESEL4.to_owned()).unwrap(), json!(98250993285u64));
341 assert_eq!(to_value(PESEL5.to_owned()).unwrap(), json!(60032417874u64));
342 }
343}