use crate::country::Code;
use crate::validator::date;
use crate::{validator, Citizen};
pub(crate) struct FranceValidator;
impl validator::CountryValidator for FranceValidator {
fn validate_id(&self, id: &str) -> bool {
let standard_id = self.sanitize_id(id);
if standard_id.len() != 15 {
return false;
}
for char in standard_id.chars() {
if !char.is_digit(10) {
return false;
}
}
let control_digit = standard_id.get(13..).unwrap().parse::<u64>().unwrap();
let partial_id = standard_id.get(0..13).unwrap().parse::<u64>().unwrap();
return control_digit == 97 - (partial_id % 97);
}
fn country_code(&self) -> Code {
return crate::country::Code::FR;
}
fn extract_citizen(&self, id: &str) -> Option<Citizen> {
if !self::FranceValidator::validate_id(&self, id) {
return None;
}
let region = get_region_of_birth(&id[5..7]);
return Some(Citizen {
gender: if String::from(&id[0..1]).parse::<u8>().unwrap() == 1_u8 {
'M'
} else {
'F'
},
year_of_birth: date::get_year_of_birth(&id[1..3]),
month_of_birth: get_month_of_birth(&id[3..5]),
day_of_birth: None,
place_of_birth: if region.is_some() {
region
} else {
get_region_of_birth(&id[5..8])
},
});
}
}
fn get_month_of_birth(code: &str) -> Option<u8> {
let month = code.parse::<u8>().unwrap();
return if month > 1_u8 && month < 13_u8 {
Some(month)
} else if month > 30_u8 && month < 43_u8 {
Some(month - 30_u8)
} else {
None
};
}
fn get_region_of_birth(code: &str) -> Option<String> {
let region = match code {
"1" => "Ain",
"2" => "Aisne",
"3" => "Allier",
"4" => "Alpes-de-Haute-Provence",
"5" => "Hautes-Alpes",
"6" => "Alpes-Maritimes",
"7" => "Ardèche",
"8" => "Ardennes",
"9" => "Ariège",
"10" => "Aube",
"11" => "Aude",
"12" => "Aveyron",
"13" => "Bouches-du-Rhône",
"14" => "Calvados",
"15" => "Cantal",
"16" => "Charente",
"17" => "Charente-Maritime",
"18" => "Cher",
"19" => "Corrèze",
"21" => "Côte-d’Or",
"22" => "Côtes-d’Armor",
"23" => "Creuse",
"24" => "Dordogne",
"25" => "Doubs",
"26" => "Drôme",
"27" => "Eure",
"28" => "Eure-et-Loir",
"29" => "Finistère",
"30" => "Gard",
"31" => "Haute-Garonne",
"32" => "Gers",
"33" => "Gironde",
"34" => "Hérault",
"35" => "Ille-et-Vilaine",
"36" => "Indre",
"37" => "Indre-et-Loire",
"38" => "Isère",
"39" => "Jura",
"40" => "Landes",
"41" => "Loir-et-Cher",
"42" => "Loire",
"43" => "Haute-Loire",
"44" => "Loire-Atlantique",
"45" => "Loiret",
"46" => "Lot",
"47" => "Lot-et-Garonne",
"48" => "Lozère",
"49" => "Maine-et-Loire",
"50" => "Manche",
"51" => "Marne",
"52" => "Haute-Marne",
"53" => "Mayenne",
"54" => "Meurthe-et-Moselle",
"55" => "Meuse",
"56" => "Morbihan",
"57" => "Moselle",
"58" => "Nièvre",
"59" => "Nord",
"60" => "Oise",
"61" => "Orne",
"62" => "Pas-de-Calais",
"63" => "Puy-de-Dôme",
"64" => "Pyrénées-Atlantiques",
"65" => "Hautes-Pyrénées",
"66" => "Pyrénées-Orientales",
"67" => "Bas-Rhin",
"68" => "Haut-Rhin",
"69" => "Circonscription départementale du Rhône",
"70" => "Haute-Saône",
"71" => "Saône-et-Loire",
"72" => "Sarthe",
"73" => "Savoie",
"74" => "Haute-Savoie",
"75" => "Paris",
"76" => "Seine-Maritime",
"77" => "Seine-et-Marne",
"78" => "Yvelines",
"79" => "Deux-Sèvres",
"80" => "Somme",
"81" => "Tarn",
"82" => "Tarn-et-Garonne",
"83" => "Var",
"84" => "Vaucluse",
"85" => "Vendée",
"86" => "Vienne",
"87" => "Haute-Vienne",
"88" => "Vosges",
"89" => "Yonne",
"90" => "Territoire de Belfort",
"91" => "Essonne",
"92" => "Hauts-de-Seine",
"93" => "Seine-Saint-Denis",
"94" => "Val-de-Marne",
"95" => "Val-d’Oise",
"971" => "Guadeloupe",
"972" => "Martinique",
"973" => "Guyane (française)",
"974" => "La Réunion",
"975" => "Saint-Pierre-et-Miquelon (voir aussi ISO 3166-1:PM)",
"976" => "Mayotte",
"977" => "Saint-Barthélemy (voir aussi ISO 3166-1:BL)",
"978" => "Saint-Martin (voir aussi ISO 3166-1:MF)",
"984" => "Terres australes et antarctiques françaises (voir aussi ISO 3166-1:TF)",
"986" => "Wallis-et-Futuna (voir aussi ISO 3166-1:WF)",
"987" => "Polynésie française (voir aussi ISO 3166-1:PF)",
"988" => "Nouvelle-Calédonie (voir aussi ISO 3166-1:NC)",
"989" => "Île de Clipperton (voir aussi ISO 3166-1:CP)",
_ => "",
};
return if region.is_empty() {
None
} else {
Some(String::from(region))
};
}
#[cfg(test)]
mod tests {
use crate::validator::france::get_region_of_birth;
use crate::validator::CountryValidator;
#[test]
fn fr_validator_requires_min_len_of_15() {
let validator = super::validator::france::FranceValidator;
assert_eq!(false, validator.validate_id("123"));
assert_eq!(false, validator.validate_id("123 456 789 0"));
}
#[test]
fn fr_validator_invalid_ids() {
let validator = super::validator::france::FranceValidator;
assert_eq!(validator.validate_id("2312760989812 01"), false);
assert_eq!(validator.validate_id("2312763214568 54"), false);
}
#[test]
fn fr_validator_valid_ids() {
let validator = super::validator::france::FranceValidator;
assert_eq!(validator.validate_id("2820819398814 09"), true);
assert_eq!(validator.validate_id("1350455179061 16"), true);
assert_eq!(validator.validate_id("2381080214568 11"), true);
assert_eq!(validator.validate_id("1880858704571 57"), true);
assert_eq!(validator.validate_id("1820897401154 75"), true);
}
#[test]
fn fr_extractor_returns_none_for_invalid_id() {
let validator = super::validator::france::FranceValidator;
assert_eq!(
validator.extract_citizen("2312760989812 01").is_none(),
true
);
}
#[test]
fn fr_extractor_returns_citizen_for_valid_ids() {
let validator = super::validator::france::FranceValidator;
let citizen_annette = validator.extract_citizen("2820819398814 09").unwrap();
assert_eq!(citizen_annette.gender, 'F');
assert_eq!(citizen_annette.year_of_birth, 1982);
assert_eq!(citizen_annette.month_of_birth.unwrap(), 8);
assert_eq!(citizen_annette.place_of_birth.unwrap(), "Corrèze");
let citizen_lothair = validator.extract_citizen("1880858704571 57").unwrap();
assert_eq!(citizen_lothair.gender, 'M');
assert_eq!(citizen_lothair.year_of_birth, 1988);
assert_eq!(citizen_lothair.month_of_birth.unwrap(), 8);
assert_eq!(citizen_lothair.place_of_birth.unwrap(), "Nièvre");
}
#[test]
fn fr_get_region_code() {
let unknown_region = get_region_of_birth("999");
let known_region = get_region_of_birth("1");
assert_eq!(unknown_region.is_none(), true);
assert_eq!(known_region.is_some(), true);
assert_eq!(known_region.unwrap(), "Ain");
}
}