1use crate::constants::mrz_utils::{
2 MRZ_TYPE1, MRZ_TYPE2, MRZ_TYPE3, TYPE1_NUMBER_OF_CHARACTERS_PER_LINE, TYPE1_TOTAL_NUMBER_OF_CHARACTERS,
3 TYPE2_NUMBER_OF_CHARACTERS_PER_LINE, TYPE2_TOTAL_NUMBER_OF_CHARACTERS, TYPE3_NUMBER_OF_CHARACTERS_PER_LINE,
4};
5use crate::parser::parser::{IMRZParser, MRZResult};
6use crate::parser::td1::TD1;
7use crate::parser::td2::TD2;
8use crate::parser::td3::TD3;
9use crate::utils::utils::check_same;
10
11mod utils;
12mod parser;
13pub mod constants;
14
15pub struct MRZParser {
16 mrz_type: usize,
17 components: Vec<String>,
18}
19
20impl MRZParser {
21 pub fn new_mrz_string_parser(mrz_str: &str) -> Self {
23 let mrz_type: usize = 0;
24 let components: Vec<String> = if mrz_str.contains('\n') {
25 mrz_str.lines().map(String::from).collect()
26 } else if mrz_str.len() == TYPE1_TOTAL_NUMBER_OF_CHARACTERS {
27 vec![
28 mrz_str[..TYPE1_NUMBER_OF_CHARACTERS_PER_LINE].to_string(),
29 mrz_str[TYPE1_NUMBER_OF_CHARACTERS_PER_LINE..2 * TYPE1_NUMBER_OF_CHARACTERS_PER_LINE].to_string(),
30 mrz_str[2 * TYPE1_NUMBER_OF_CHARACTERS_PER_LINE..].to_string(),
31 ]
32 } else if mrz_str.len() == TYPE2_TOTAL_NUMBER_OF_CHARACTERS {
33 vec![
34 mrz_str[..TYPE2_NUMBER_OF_CHARACTERS_PER_LINE].to_string(),
35 mrz_str[TYPE2_NUMBER_OF_CHARACTERS_PER_LINE..].to_string(),
36 ]
37 } else {
38 vec![
39 mrz_str[..TYPE3_NUMBER_OF_CHARACTERS_PER_LINE].to_string(),
40 mrz_str[TYPE3_NUMBER_OF_CHARACTERS_PER_LINE..].to_string(),
41 ]
42 };
43
44 MRZParser { mrz_type, components }
45 }
46
47 pub fn new_mrz_line_parser(mrz_lines: Vec<String>) -> Self {
49 let mrz_type: usize = 0;
50 MRZParser {
51 mrz_type,
52 components: mrz_lines,
53 }
54 }
55
56 pub fn get_mrz_type(&mut self) -> Result<usize, &'static str> {
58 self.validate()?;
59 Ok(self.mrz_type)
60 }
61
62 pub fn parse(&mut self) -> Result<MRZResult, &'static str> {
64 self.validate()?;
65
66 let mrz_parser: Box<dyn IMRZParser> = match self.mrz_type {
67 MRZ_TYPE1 => Box::new(TD1::new()),
68 MRZ_TYPE2 => Box::new(TD2::new()),
69 MRZ_TYPE3 => Box::new(TD3::new()),
70 _ => return Err("invalid mrz type"),
71 };
72
73 mrz_parser.parse(&self.components)
74 }
75
76 fn validate(&mut self) -> Result<(), &'static str> {
78 let mut mrz_type: usize = 0;
79
80 match self.components.len() {
81 3 => {
82 for line in &self.components {
83 if line.len() != TYPE1_NUMBER_OF_CHARACTERS_PER_LINE {
84 return Err("invalid TD1 format line length");
85 }
86 }
87 mrz_type = MRZ_TYPE1;
88 }
89 2 => {
90 let mut character_count = Vec::new();
91 for line in &self.components {
92 if line.len() != TYPE2_NUMBER_OF_CHARACTERS_PER_LINE
93 && line.len() != TYPE3_NUMBER_OF_CHARACTERS_PER_LINE
94 {
95 return Err("invalid mrz line length");
96 }
97 character_count.push(line.len());
98 }
99
100 if check_same(&character_count) && character_count[0] == TYPE2_NUMBER_OF_CHARACTERS_PER_LINE {
101 mrz_type = MRZ_TYPE2;
102 }
103 if check_same(&character_count) && character_count[0] == TYPE3_NUMBER_OF_CHARACTERS_PER_LINE {
104 mrz_type = MRZ_TYPE3;
105 }
106 }
107 _ => return Err("invalid mrz line length"),
108 }
109
110 self.mrz_type = mrz_type;
111 Ok(())
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn test_td1_vec() {
121 let mrz_string: Vec<String> = vec![
122 "I<UTOD231458907<<<<<<<<<<<<<<<".to_string(),
123 "7408122F1204159UTO<<<<<<<<<<<6".to_string(),
124 "ERIKSSON<<ANNA<MARIA<<<<<<<<<<".to_string(),
125 ];
126
127 let mut parser = MRZParser::new_mrz_line_parser(mrz_string);
128 let result = parser.parse().unwrap();
129 assert_eq!(result.is_valid, true);
130 println!("{:?}", result)
131 }
132
133 #[test]
134 fn test_td1_str() {
135 let mrz_string: &str = "\
136 I<UTOD231458907<<<<<<<<<<<<<<<\n\
137 7408122F1204159UTO<<<<<<<<<<<6\n\
138 ERIKSSON<<ANNA<MARIA<<<<<<<<<<";
139 let mut parser = MRZParser::new_mrz_string_parser(mrz_string);
140 let result = parser.parse().unwrap();
141 assert_eq!(result.is_valid, true);
142 println!("{:?}", result)
143 }
144}