pesel_rs/
bit_fields.rs

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/// Stores each section of the PESEL in the following layout:
16///
17/// `------------------------------------------------------------------------`
18/// `| 7 bits | YY | 5 bits | MM | 5 bits | DD | 5 bits | OOOO | 5 bits | C |`
19/// `------------------------------------------------------------------------`
20///
21/// In between bits are unused. Extracting each field is done using bitwise operations.
22/// You can get the human readable number using `u64::from`.
23///
24/// Used when frequently reading individual fields.
25#[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}