pub mod data;
use data::{get_amphoe, get_person_type, get_province, Amphoe, PersonType, Province};
#[derive(Debug)]
pub struct IdMeaning {
pub person_type: PersonType,
pub province: Option<Province>,
pub amphoe: Option<Amphoe>,
pub is_valid: bool,
}
pub fn get_id_meaning(id: &str) -> Option<IdMeaning> {
if id.len() != 13 || !id.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
let person_type_code = id[0..1].parse::<u8>().ok()?;
let province_code = id[1..3].parse::<u8>().ok()?;
let amphoe_code = id[1..5].parse::<u16>().ok()?;
let person_type = get_person_type(person_type_code)?;
let province = get_province(province_code);
let amphoe = get_amphoe(amphoe_code);
let is_valid = verify_id(id);
Some(IdMeaning {
person_type,
province,
amphoe,
is_valid,
})
}
pub fn verify_id(id: &str) -> bool {
if id.len() != 13 || !id.bytes().all(|b| b.is_ascii_digit()) {
return false;
}
let digits: Vec<u32> = id.bytes().map(|b| (b - b'0') as u32).collect();
let total: u32 = digits[..12]
.iter()
.enumerate()
.map(|(i, &d)| d * (13 - i as u32))
.sum();
let calculated_checksum = (11 - total % 11) % 10;
calculated_checksum == digits[12]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_id() {
assert!(verify_id("1112034563562"));
}
#[test]
fn test_invalid_checksum() {
assert!(!verify_id("1101700230705"));
}
#[test]
fn test_too_short() {
assert!(!verify_id("110170023073"));
}
#[test]
fn test_contains_letters() {
assert!(!verify_id("11017002070d3"));
assert!(!verify_id("rytege54fsfsf"));
}
#[test]
fn test_single_char() {
assert!(!verify_id("0"));
assert!(!verify_id("-"));
}
#[test]
fn test_empty() {
assert!(!verify_id(""));
}
#[test]
fn test_garbage() {
assert!(!verify_id("blablabla"));
}
}