#validation #polish #identification #poland #bit-field #human-readable #pesel

pesel-rs

PESEL validation and detail extraction with multiple data layout implementations

2 releases

0.1.3 Oct 30, 2024
0.1.2 Oct 30, 2024
0.1.1 Oct 30, 2024
0.1.0 Oct 27, 2024

#238 in Authentication

MIT/Apache

44KB
929 lines

PESEL validation and detail extraction with multiple data layout implementations.

Crates.io Version Static Badge Crates.io License

Definitions

PESEL: YYMMDDOOOOC

  • YY - Last two digits of year of birth
  • MM - Month of birth (shifted depending on year of birth as shown by the table below)
Year 1800 - 1899 1900 - 1999 2000 - 2099 2100 - 2199 2200 - 2299
Month shift +80 0 +20 +40 +60
  • DD - Day of birth
  • OOOO - Ordinal number, where the last digit denotes the gender ([0, 2, 4, 6, 8] = female, [1, 3, 5, 7, 9] = male)
  • C - Control number

Usage

There are two PESEL structs provided by the crate, both implementing the PeselTrait.

  • crate::bit_fields::Pesel - Stores each section of the PESEL in the following layout: 7 bits | YY | 5 bits | MM | 5 bits | DD | 5 bits | OOOO | 5 bits | C, where in between bits are unused. Extracting each field is done using bitwise operations. You can get the human readable number using u64::from.

  • crate::human_redable::Pesel - Stores the PESEL as a plain number, extracting each field requires modulo and division operations, if often accessing individual fields is important to you, you should probably use crate::bit_fields::Pesel.

If you just need to validate a number or extract a specific section without using the structs, you could use functions in the lib root. Most of these functions won't check if the value they're returning is valid, unlike the structs who are guaranteed to always return a valid value.

Examples

Function that takes a name and welcomes the person based on date of birth and gender from the PESEL. Implemented using crate::bit_fields::Pesel because we're mostly reading the fields.

use pesel_rs::{prelude::*, bit_fields::Pesel};

fn welcome(first_name: &str, pesel: u64) {
    match Pesel::try_from(pesel) {
        Ok(pesel) => {
            if pesel.date_of_birth() > NaiveDate::from_ymd_opt(2015, 1, 1).unwrap() {
                let gender = if pesel.gender() == Gender::Male { "boy" } else { "girl" };
                println!("Wow {first_name}! You're such a young {gender}!");
            } else {
                println!("{first_name}, you're very old, I'm sorry 😞");
            }
        }
        Err(_) => println!("Huh, what you gave me doesn't seem to be a valid pesel {first_name}..."),
    }
}

Function finding a pesel with the oldest date of birth. Working with a generic PESEL, we introduce additional bounds (required by PeselTrait).

use pesel_rs::prelude::*;

fn oldest<T: PeselTrait>(pesels: &[T])
where
    u64: From<T>,
    for<'a> u64: From<&'a T>
{
    assert!(pesels.len() > 0);

    let mut oldest_index = 0;
    pesels.iter().skip(1).enumerate().for_each(|(i, pesel)| {
        if pesels[oldest_index].date_of_birth() < pesel.date_of_birth() {
            oldest_index = i;
        }
    });

    let date_of_birth = pesels[oldest_index].date_of_birth();
    println!("PESEL nr. {oldest_index} is the oldest! Born at {date_of_birth}")
}

Dependencies

~1.2–2MB
~36K SLoC