Cargo.toml
regex = "1.5.4"
# 主要用来校验润年
time = {version= "0.3.9", features = ["formatting","parsing"] }
use std::collections::HashMap;
use time::{Date, format_description};
fn main() {
let validate = IdCardValidate::init();
// 这个生成的身份证号符合身份证规律,但不符合润年等日期规律
assert_eq!(generate_cert("110825199102294010").unwrap(), "11082519910229401X");
// 不符合润年等日期规律
assert_eq!(validate.validate_idcard("11082519910229401X"), false);
// 不用润年日期生成一个符合的身份证号
println!("可用的身份证号 {}", generate_cert("110825199102284010").unwrap());
// 不用润年日期生成一个符合的身份证号,通过
assert_eq!(validate.validate_idcard("110825199102284014"), true);
}
pub struct IdCardValidate(HashMap<i8, &'static str>);
impl IdCardValidate {
pub fn init() -> Self {
let mut provs = HashMap::new();
provs.insert(11, "北京");
provs.insert(12, "天津");
provs.insert(13, "河北");
provs.insert(14, "山西");
provs.insert(15, "内蒙古");
provs.insert(21, "辽宁");
provs.insert(22, "吉林");
provs.insert(23, "黑龙江");
provs.insert(31, "上海");
provs.insert(32, "江苏");
provs.insert(33, "浙江");
provs.insert(34, "安徽");
provs.insert(35, "福建");
provs.insert(36, "江西");
provs.insert(37, "山东");
provs.insert(41, "河南");
provs.insert(42, "湖北 ");
provs.insert(43, "湖南");
provs.insert(44, "广东");
provs.insert(45, "广西");
provs.insert(46, "海南");
provs.insert(50, "重庆");
provs.insert(51, "四川");
provs.insert(52, "贵州");
provs.insert(53, "云南");
provs.insert(54, "西藏 ");
provs.insert(61, "陕西");
provs.insert(62, "甘肃");
provs.insert(63, "青海");
provs.insert(64, "宁夏");
provs.insert(65, "新疆");
provs.insert(71, "台湾");
provs.insert(81, "香港");
provs.insert(82, "澳门");
Self(provs)
}
pub fn validate_idcard(&self, cert: &str) -> bool {
if check_code(cert) {
let date = &cert[6..14];
if check_date(date) {
return self.check_prov(&cert[0..2]);
}
}
false
}
fn check_prov(&self, prov: &str) -> bool {
let pattern = "^[1-9][0-9]";
validate(pattern, prov) && self.0.contains_key(&prov.parse().unwrap())
}
}
fn check_code(cert: &str) -> bool {
let regex = r"^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$";
if validate(regex, cert) {
return match check_sum(cert).map(|sum| sum.eq(&cert[17..])) {
Ok(result) => result,
Err(_) => false
};
}
return false;
}
pub fn generate_cert(cert: &str) -> Result<String, String> {
let regex = r"^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$";
if validate(regex, cert) {
return check_sum(cert).map(|sum| format!("{}{}", cert[0..17].to_string(), sum));
}
Err("身份证号错误".to_string())
}
fn check_date(date: &str) -> bool {
let pattern = r#"^(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)$"#;
if validate(pattern, date) {
let format = format_description::parse("[year][month][day]").unwrap();
// rust只需要验证日期正确即可排除用户随便写出19900229这样非法日期
return Date::parse(date, &format).is_ok();
}
return false;
}
fn check_sum(cert: &str) -> Result<&'static str, String> {
if cert.len() > 17 {
let factor = [7u32, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
let mut sum = 0;
let mut chars = cert.chars();
for i in 0..17 {
sum += (chars.next().unwrap() as u32 -48) * factor[i];
}
let parity = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"];
Ok(parity[sum as usize % 11])
} else {
Err(format!("身份证号长度{}<18位", cert.len()))
}
}
fn validate(pattern: &str, content: &str) -> bool {
use regex::Regex;
let re = Regex::new(pattern).unwrap();
re.is_match(content)
}
#[test]
fn test(){
let regex = r"^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$";
// 最后一位是造的,可以过正则,但过不了身份证规律
assert_eq!(validate(regex, "110825199102294010"),true);
}