inid_rs/
lib.rs

1//! INID (Iran National Identifier)
2//! This crate can generate INID and also even can generate INID with prefix.
3//! Another feature of this crate is that it can check a INID is valid or not.
4
5use std::{vec};
6use error::INIDError;
7use rand::Rng;
8
9pub mod error;
10
11const INID_LENGTH:u8 = 10;
12const DECIMAL_RADIX:u8 = 10;
13type INID = Vec<u8>;
14
15
16/// Every INID has a checksum number at the end
17/// This function Generates that checksum for an INID
18fn generate_checksum(nid:&INID) -> u8{
19    let mut n_sum: u16 = 0;
20        for p in (2..=10).rev(){
21            n_sum += (nid[10 - p] as u16) * (p as u16);
22        }
23    let r = n_sum % 11 ;
24    let checksum:u8 = if r < 2 {r as u8} else {11 - r as u8};
25    checksum
26}
27
28/// Generates INID, also can generate with prefix
29/// 
30/// # Examples
31/// ```
32/// let inid =rs_inid::generate_id(None).unwrap();
33/// // Or we can pass a prefix to it
34/// let inid = rs_inid::generate_id(Some(&"123".to_string())).unwrap();
35/// ```
36pub fn generate_id(prefix:Option<&String>) -> Result<String,INIDError>{
37
38    match prefix {
39       Some(prefix) =>{
40
41        let mut inid = convert_str_to_inid(&prefix)?;
42
43        for _ in 0..9-inid.len(){
44            let random_number = rand::thread_rng().gen_range(0..=9);
45            inid.push(random_number);
46        }
47
48        let checksum = generate_checksum(&inid);
49        inid.push(checksum);
50
51        return Ok(convert_inid_to_str(&inid))
52       },
53       None =>{
54        let mut inid :Vec<u8> = vec![];
55
56        for _ in 1..=9{
57            let random_number = rand::thread_rng().gen_range(0..=9);
58            inid.push(random_number);
59        }
60
61        let checksum = generate_checksum(&inid);
62        inid.push(checksum);
63
64        Ok(convert_inid_to_str(&inid))
65       }
66   }
67}
68
69/// Checks an INID is valid or not
70/// 
71/// # Examples
72/// ```
73/// let inid = String::from("1234567890");
74/// let res = rs_inid::check_inid(&inid).unwrap();
75/// assert_eq!(res,false);
76/// ```
77pub fn check_inid(s_inid:&String) -> Result<bool,INIDError>{
78    if s_inid.chars().count() != INID_LENGTH as usize{
79        return Err(INIDError::InvalidLength)
80    }
81    if !s_inid.chars().all(|c| c.is_numeric()){
82        return Err(INIDError::NotNumerical)
83    }
84    let inid = convert_str_to_inid(s_inid)?;
85    Ok(generate_checksum(&inid) == inid[inid.len()-1])   
86}
87
88/// Converts an INID to String
89/// INID is a alias for Vec<u8>
90fn convert_inid_to_str(inid:&INID) -> String{
91    let mut s = String::from("");
92    for item in inid{
93        s.push_str(&item.to_string());
94    }
95    s
96}
97/// Converts a String to an INID
98fn convert_str_to_inid(input:&String) -> Result<INID,INIDError>{
99    
100    let mut nid: Vec<u8> = vec![];
101    for s in input.chars(){
102        if s == ' '{
103            continue;
104        }
105        match s.to_digit(DECIMAL_RADIX as u32){
106            Some(char_digit) => nid.push(char_digit as u8),
107            None => return Err(error::INIDError::NotNumerical),
108        }
109    }
110    Ok(nid)
111}
112
113#[cfg(test)]
114mod tests {
115
116    use super::*;
117
118    #[test]
119    fn generate_id_test() {
120        // Test without prefix
121        let result = generate_id(None);
122        match result{
123            Ok(v) => {
124                
125                let inid = match convert_str_to_inid(&v){
126                    Ok(v) =>v,
127                    Err(e) => panic!("{}",e)
128                };
129
130                let res_len = inid.len();
131
132                // Check length
133                assert_eq!(res_len,10);
134                
135                // Get last char of String
136                assert_eq!(inid[res_len -1],generate_checksum(&inid))
137            },
138            Err(e) => {panic!("{}",e)},
139        }
140        // Test with prefix
141        let result = generate_id(Some(&"123".to_string()));
142        match result{
143            Ok(v) => {
144                
145                let inid = match convert_str_to_inid(&v){
146                    Ok(v) =>v,
147                    Err(e) => panic!("{}",e)
148                };
149
150                let res_len = inid.len();
151
152                // Check length
153                assert_eq!(res_len,10);
154                
155                // Get last char of String
156                assert_eq!(inid[res_len -1],generate_checksum(&inid))
157            },
158            Err(e) => {panic!("{}",e)},
159        }
160    }
161
162    #[test]
163    fn check_id_and_generate_checksum_test(){
164        let ids:Vec<INID> = vec![vec![2,3,5,7,2,1,4,8,1,3],vec![8,7,4,7,6,2,3,6,6,7],vec![4,5,7,5,9,0,0,7,6,1]];
165        for item in ids{
166            let checksum = generate_checksum(&item);
167            assert_eq!(checksum,item[item.len()-1]);
168        }
169    }
170    
171}