1use once_cell::sync::Lazy;
2use regex::Regex;
3
4#[derive(Debug)]
7pub struct MRZCountryPattern {
8 pub country_code: &'static str,
9 pub id_prefix: &'static str,
10 pub document_number_pattern: Regex,
11 pub document_number_start_index: usize,
12}
13
14static PATTERNS: Lazy<Vec<MRZCountryPattern>> = Lazy::new(|| {
17 vec![
18 MRZCountryPattern {
19 country_code: "AUT",
20 id_prefix: "IDAUT",
21 document_number_pattern: Regex::new(r"^([0-9]{10})").unwrap(),
22 document_number_start_index: 5,
23 },
24 MRZCountryPattern {
25 country_code: "BEL",
26 id_prefix: "IDBEL",
27 document_number_pattern: Regex::new(r"^([A-Z][0-9]{3}[A-Z][0-9]{6})").unwrap(),
28 document_number_start_index: 5,
29 },
30 MRZCountryPattern {
31 country_code: "BGR",
32 id_prefix: "IDBGR",
33 document_number_pattern: Regex::new(r"^([0-9]{10})").unwrap(),
34 document_number_start_index: 5,
35 },
36 MRZCountryPattern {
37 country_code: "CYP",
38 id_prefix: "IDCYP",
39 document_number_pattern: Regex::new(r"^([0-9]{9})").unwrap(),
40 document_number_start_index: 5,
41 },
42 MRZCountryPattern {
43 country_code: "CZE",
44 id_prefix: "IDCZE",
45 document_number_pattern: Regex::new(r"^([A-Z]?[0-9]{8,9})").unwrap(),
46 document_number_start_index: 5,
47 },
48 MRZCountryPattern {
49 country_code: "DEU",
50 id_prefix: "IDDEU",
51 document_number_pattern: Regex::new(r"^([A-Z0-9]{9})").unwrap(),
52 document_number_start_index: 5,
53 },
54 MRZCountryPattern {
55 country_code: "DNK",
56 id_prefix: "IDDNK",
57 document_number_pattern: Regex::new(r"^([0-9]{9})").unwrap(),
58 document_number_start_index: 5,
59 },
60 MRZCountryPattern {
61 country_code: "ESP",
62 id_prefix: "IDESP",
63 document_number_pattern: Regex::new(r"^([0-9]{8}[A-Z])").unwrap(),
64 document_number_start_index: 5,
65 },
66 MRZCountryPattern {
67 country_code: "EST",
68 id_prefix: "IDEST",
69 document_number_pattern: Regex::new(r"^([A-Z]{0,2}[0-9]{8})").unwrap(),
70 document_number_start_index: 5,
71 },
72 MRZCountryPattern {
73 country_code: "FIN",
74 id_prefix: "IDFIN",
75 document_number_pattern: Regex::new(r"^([0-9]{14})").unwrap(),
76 document_number_start_index: 5,
77 },
78 MRZCountryPattern {
79 country_code: "FRA",
80 id_prefix: "IDFRA",
81 document_number_pattern: Regex::new(r"^([A-Z0-9]{12})").unwrap(),
82 document_number_start_index: 5,
83 },
84 MRZCountryPattern {
85 country_code: "GBR",
86 id_prefix: "IDGBR",
87 document_number_pattern: Regex::new(r"^([0-9]{13})").unwrap(),
88 document_number_start_index: 5,
89 },
90 MRZCountryPattern {
91 country_code: "GRC",
92 id_prefix: "IDGRC",
93 document_number_pattern: Regex::new(r"^([A-Z]{0,2}[0-9]{8,10})").unwrap(),
94 document_number_start_index: 5,
95 },
96 MRZCountryPattern {
97 country_code: "HRV",
98 id_prefix: "IDHRV",
99 document_number_pattern: Regex::new(r"^([0-9]{11})").unwrap(),
100 document_number_start_index: 5,
101 },
102 MRZCountryPattern {
103 country_code: "HUN",
104 id_prefix: "IDHUN",
105 document_number_pattern: Regex::new(r"^([0-9]{6}[A-Z]{2})").unwrap(),
106 document_number_start_index: 5,
107 },
108 MRZCountryPattern {
109 country_code: "IRL",
110 id_prefix: "IDIRL",
111 document_number_pattern: Regex::new(r"^([A-Z0-9]{12})").unwrap(),
112 document_number_start_index: 5,
113 },
114 MRZCountryPattern {
115 country_code: "ITA",
116 id_prefix: "IDITA",
117 document_number_pattern: Regex::new(r"^([A-Z0-9]{10})").unwrap(),
118 document_number_start_index: 5,
119 },
120 MRZCountryPattern {
121 country_code: "LTU",
122 id_prefix: "IDLTU",
123 document_number_pattern: Regex::new(r"^([0-9]{8})").unwrap(),
124 document_number_start_index: 5,
125 },
126 MRZCountryPattern {
127 country_code: "LUX",
128 id_prefix: "IDLUX",
129 document_number_pattern: Regex::new(r"^([A-Z]{0,2}[0-9]{8,10})").unwrap(),
130 document_number_start_index: 5,
131 },
132 MRZCountryPattern {
133 country_code: "LVA",
134 id_prefix: "IDLVA",
135 document_number_pattern: Regex::new(r"^([A-Z]?[0-9]{8,9})").unwrap(),
136 document_number_start_index: 5,
137 },
138 MRZCountryPattern {
139 country_code: "MLT",
140 id_prefix: "IDMLT",
141 document_number_pattern: Regex::new(r"^([A-Z]?[0-9]{8,9})").unwrap(),
142 document_number_start_index: 5,
143 },
144 MRZCountryPattern {
145 country_code: "NLD",
146 id_prefix: "IDNLD",
147 document_number_pattern: Regex::new(r"^([A-Z]{2}[0-9]{7})").unwrap(),
148 document_number_start_index: 5,
149 },
150 MRZCountryPattern {
151 country_code: "POL",
152 id_prefix: "IDPOL",
153 document_number_pattern: Regex::new(r"^([A-Z]{0,3}[0-9]{8,9})").unwrap(),
154 document_number_start_index: 5,
155 },
156 MRZCountryPattern {
157 country_code: "PRT",
158 id_prefix: "IDPRT",
159 document_number_pattern: Regex::new(r"^([A-Z]{0,2}[0-9]{8})").unwrap(),
160 document_number_start_index: 5,
161 },
162 MRZCountryPattern {
163 country_code: "ROU",
164 id_prefix: "IDROU",
165 document_number_pattern: Regex::new(r"^([A-Z]{2}[0-9]{8,9})").unwrap(),
166 document_number_start_index: 5,
167 },
168 MRZCountryPattern {
169 country_code: "SVK",
170 id_prefix: "IDSVK",
171 document_number_pattern: Regex::new(r"^([0-9]{6}[A-Z][0-9]{3})").unwrap(),
172 document_number_start_index: 5,
173 },
174 MRZCountryPattern {
175 country_code: "SVN",
176 id_prefix: "IDSVN",
177 document_number_pattern: Regex::new(r"^([A-Z]?[0-9]{9,10})").unwrap(),
178 document_number_start_index: 5,
179 },
180 MRZCountryPattern {
181 country_code: "SWE",
182 id_prefix: "IDSWE",
183 document_number_pattern: Regex::new(r"^([A-Z]?[0-9]{8,9})").unwrap(),
184 document_number_start_index: 5,
185 },
186 ]
187});
188
189pub fn get_country_pattern(first_line: &str) -> Option<&'static MRZCountryPattern> {
192 PATTERNS
193 .iter()
194 .find(|p| first_line.starts_with(p.id_prefix))
195}
196
197pub fn patterns() -> &'static [MRZCountryPattern] {
199 PATTERNS.as_slice()
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn find_known_prefix() {
208 let first = "IDGBR123456789012";
209 let p = get_country_pattern(first).expect("pattern found");
210 assert_eq!(p.country_code, "GBR");
211 }
212
213 #[test]
214 fn no_match_returns_none() {
215 let first = "UNKNOWNPREFIX";
216 assert!(get_country_pattern(first).is_none());
217 }
218}