taiwan_id/
lib.rs

1/// Check if the given string is a valid ID number.
2///
3/// # Examples
4///
5/// ```
6/// assert_eq!(true, taiwan_id::is_valid("A123456789"));
7/// assert_eq!(false, taiwan_id::is_valid("A987654321"));
8/// ```
9pub fn is_valid(id: &str) -> bool {
10    if id.len() != 10 {
11        return false;
12    }
13    let mut a: [u8; 11] = [0; 11];
14    let mut iter = id.chars();
15    let first_letter = iter.next().unwrap();
16    if let 'A'...'Z' = first_letter {
17        let pair = code_map(first_letter);
18        a[0] = pair[0];
19        a[1] = pair[1];
20    } else {
21        return false;
22    }
23
24    let mut i = 2;
25    for c in iter {
26        if let '0'...'9' = c {
27            a[i] = c as u8 - '0' as u8;
28            i += 1;
29        } else {
30            return false;
31        }
32    }
33    sum(&a) % 10 == 0
34}
35
36/// Generate a random ID with the given prefix.
37/// Same as `generate_prefix("")`
38pub fn generate() -> String {
39    generate_prefix("")
40}
41
42/// Generate a random ID with the given prefix.
43///
44/// For more information, please refere to [wiki](https://zh.wikipedia.org/wiki/%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E5%9C%8B%E6%B0%91%E8%BA%AB%E5%88%86%E8%AD%89#%E9%A9%97%E8%AD%89%E8%A6%8F%E5%89%87)
45///
46/// # Examples
47///
48/// ```
49/// // Generate a random ID for Taipei City:
50/// let id = taiwan_id::generate_prefix("A");
51/// assert!(id.starts_with("A"));
52/// assert!(taiwan_id::is_valid(&id));
53///
54/// // Generate a random female ID for Taipei City
55/// let id = taiwan_id::generate_prefix("A2");
56/// assert!(id.starts_with("A2"));
57/// assert!(taiwan_id::is_valid(&id));
58/// ```
59pub fn generate_prefix(prefix: &str) -> String {
60    if prefix.len() > 9 {
61        panic!("prefix is too long");
62    }
63
64    use rand::Rng;
65    let mut rng = rand::thread_rng();
66
67    if prefix.is_empty() {
68        return generate_prefix(&format!(
69            "{}{}",
70            rng.gen_range(b'A', b'Z') as char,
71            rng.gen_range(1, 3)
72        ));
73    }
74
75    if prefix.len() == 1 {
76        return generate_prefix(&format!("{}{}", prefix, rng.gen_range(1, 3)));
77    }
78
79    let first_letter = prefix.chars().next().unwrap();
80    if let 'A'...'Z' = first_letter {
81    } else {
82        panic!("prefix is not valid")
83    }
84
85    let pair = code_map(first_letter);
86    let mut a: [u8; 11] = [pair[0], pair[1], 0, 0, 0, 0, 0, 0, 0, 0, 0];
87    let mut a_index = 2;
88    for i in prefix[1..].chars() {
89        if let '0'...'9' = i {
90        } else {
91            panic!("prefix is not valid")
92        }
93        a[a_index] = i as u8 - '0' as u8;
94        a_index += 1;
95    }
96    let len = a.len() - 1;
97    for i in &mut a[a_index..len] {
98        *i = rng.gen::<u8>() % 10;
99    }
100    a[len] = (10 - (sum(&a) % 10) as u8) % 10;
101    a[prefix.len() + 1..]
102        .iter()
103        .fold(String::from(prefix), |s, i| s + &i.to_string())
104}
105
106fn sum(ary: &[u8]) -> u16 {
107    static MULTIPLIERS: [u8; 11] = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1];
108    ary.iter().enumerate().fold(0, |acc, (index, value)| {
109        acc + (MULTIPLIERS[index] * value) as u16
110    })
111}
112
113fn code_map(c: char) -> [u8; 2] {
114    static CODE_MAP: [[u8; 2]; 26] = [
115        [1, 0],
116        [1, 1],
117        [1, 2],
118        [1, 3],
119        [1, 4],
120        [1, 5],
121        [1, 6],
122        [1, 7],
123        [3, 4],
124        [1, 8],
125        [1, 9],
126        [2, 0],
127        [2, 1],
128        [2, 2],
129        [3, 5],
130        [2, 3],
131        [2, 4],
132        [2, 5],
133        [2, 6],
134        [2, 7],
135        [2, 8],
136        [2, 9],
137        [3, 2],
138        [3, 0],
139        [3, 1],
140        [3, 3],
141    ];
142    CODE_MAP[(c as u8 - 'A' as u8) as usize]
143}
144
145#[cfg(test)]
146mod tests {
147    #[test]
148    fn is_valid() {
149        assert!(super::is_valid("A123456789"));
150        assert!(!super::is_valid("A1234567899"));
151        assert!(!super::is_valid("Z123456789"));
152        assert!(!super::is_valid(""));
153        assert!(!super::is_valid("A一二三四五六七八九"));
154    }
155
156    #[test]
157    fn generate() {
158        let id = super::generate_prefix("A1");
159        assert!(id.starts_with("A1"));
160        assert!(super::is_valid(&id));
161
162        let id = super::generate_prefix("A");
163        assert!(id.starts_with("A"));
164        assert!(super::is_valid(&id));
165
166        let id = super::generate_prefix("");
167        assert!(super::is_valid(&id));
168
169        let id = super::generate();
170        assert!(super::is_valid(&id));
171    }
172}