1use std::collections::HashMap;
30
31use regex::Regex;
32use urlencoding::decode;
33
34use once_cell::sync::Lazy;
35
36use crate::{common::Result, exceptions::Exceptions, RXingResult};
37
38use super::{
39 AddressBookAUResultParser, AddressBookDoCoMoResultParser, BizcardResultParser,
40 BookmarkDoCoMoResultParser, EmailAddressResultParser, EmailDoCoMoResultParser,
41 ExpandedProductResultParser, GeoResultParser, ISBNResultParser, ParsedClientResult,
42 ProductResultParser, SMSMMSResultParser, SMSTOMMSTOResultParser, SMTPResultParser,
43 TelResultParser, TextParsedRXingResult, URIResultParser, URLTOResultParser, VCardResultParser,
44 VEventResultParser, VINResultParser, WifiResultParser,
45};
46
47pub type ParserFunction = dyn Fn(&RXingResult) -> Option<ParsedClientResult>;
94
95static DIGITS: Lazy<Regex> = Lazy::new(|| Regex::new("\\d+").unwrap());
96
97const AMPERSAND: &str = "&"; const EQUALS: &str = "="; const BYTE_ORDER_MARK: &str = "\u{feff}"; pub fn getMassagedText(result: &RXingResult) -> String {
105 result
106 .getText()
107 .trim_start_matches(BYTE_ORDER_MARK)
108 .to_owned()
109 }
114
115pub fn parse_result_with_parsers(
116 the_rxing_result: &RXingResult,
117 parsers: &[&ParserFunction],
118) -> ParsedClientResult {
119 for parser in parsers {
120 let result = parser(the_rxing_result);
121 if let Some(res) = result {
122 return res;
123 }
124 }
125 parseRXingResult(the_rxing_result)
126}
127
128pub fn parse_result_with_parser<F: Fn(&RXingResult) -> Option<ParsedClientResult>>(
129 the_rxing_result: &RXingResult,
130 parser: F,
131) -> Option<ParsedClientResult> {
132 parser(the_rxing_result)
133}
134
135pub fn parseRXingResult(the_rxing_result: &RXingResult) -> ParsedClientResult {
136 let PARSERS: [&ParserFunction; 20] = [
137 &BookmarkDoCoMoResultParser::parse,
138 &AddressBookDoCoMoResultParser::parse,
139 &EmailDoCoMoResultParser::parse,
140 &AddressBookAUResultParser::parse,
141 &VCardResultParser::parse,
142 &BizcardResultParser::parse,
143 &VEventResultParser::parse,
144 &EmailAddressResultParser::parse,
145 &SMTPResultParser::parse,
146 &TelResultParser::parse,
147 &SMSMMSResultParser::parse,
148 &SMSTOMMSTOResultParser::parse,
149 &GeoResultParser::parse,
150 &WifiResultParser::parse,
151 &URLTOResultParser::parse,
152 &URIResultParser::parse,
153 &ISBNResultParser::parse,
154 &ProductResultParser::parse,
155 &ExpandedProductResultParser::parse,
156 &VINResultParser::parse,
157 ];
158
159 for parser in PARSERS {
160 let result = parser(the_rxing_result);
161 if let Some(res) = result {
162 return res;
163 }
164 }
165 ParsedClientResult::TextResult(TextParsedRXingResult::new(
172 the_rxing_result.getText().to_owned(),
173 String::default(),
174 ))
175}
176
177pub fn maybe_append_string(value: &str, result: &mut String) {
178 if !value.is_empty() {
179 if !result.is_empty() {
180 result.push('\n');
181 }
182 result.push_str(value);
183 }
184}
185
186pub fn maybe_append_multiple(value: &[String], result: &mut String) {
187 for s in value {
188 if !s.is_empty() {
190 if !result.is_empty() {
191 result.push('\n');
192 }
193 result.push_str(s);
194 }
195 }
196}
197
198#[inline(always)]
199pub fn maybeWrap(value: Option<String>) -> Option<Vec<String>> {
200 if value.is_none() {
201 None
202 } else {
203 Some(vec![value.unwrap()])
204 }
205}
206
207pub fn unescapeBackslash(escaped: &str) -> String {
208 let backslash = escaped.find('\\');
209 if backslash.is_none() {
210 return escaped.to_owned();
211 }
212 let max = escaped.chars().count();
213 let backslash = backslash.unwrap_or(0);
214 let mut unescaped = escaped.chars().take(backslash).collect::<String>();
215 unescaped.reserve(max - 1);
216 let mut nextIsEscaped = false;
217 for c in escaped.chars().skip(backslash) {
218 if nextIsEscaped || c != '\\' {
219 unescaped.push(c);
220 nextIsEscaped = false;
221 } else {
222 nextIsEscaped = true;
223 }
224 }
225
226 unescaped
227}
228
229#[inline(always)]
230pub fn parseHexDigit(c: char) -> Option<u32> {
231 c.to_digit(16)
232 }
247
248#[inline(always)]
249pub fn isStringOfDigits(value: &str, length: usize) -> bool {
250 !value.is_empty() && length > 0 && length == value.len() && DIGITS.is_match(value)
251}
252
253pub fn isSubstringOfDigits(value: &str, offset: usize, length: usize) -> bool {
254 if value.is_empty() || length == 0 {
255 return false;
256 }
257 let max = offset + length;
258
259 let sub_seq: String = value.chars().skip(offset).take(length).collect(); let is_a_match = if let Some(mtch) = DIGITS.find(&sub_seq) {
262 mtch.start() == 0 && mtch.end() == sub_seq.chars().count()
263 } else {
264 false
265 };
266
267 value.len() >= max && is_a_match
268}
269
270pub fn parseNameValuePairs(uri: &str) -> Option<HashMap<String, String>> {
271 let paramStart = uri.find('?');
272 paramStart?;
273 let mut result = HashMap::with_capacity(3);
274 let paramStart = paramStart.unwrap_or(0);
275
276 let sub_str = &uri[paramStart + 1..]; let list = sub_str.split(AMPERSAND);
278 for keyValue in list {
279 appendKeyValue(keyValue, &mut result);
280 }
281 Some(result)
282}
283
284pub fn appendKeyValue(keyValue: &str, result: &mut HashMap<String, String>) {
285 let keyValueTokens = keyValue.split(EQUALS); let kvp: Vec<&str> = keyValueTokens.take(2).collect();
288 if let [key, value] = kvp[..] {
289 let p_value = urlDecode(value).unwrap_or_else(|_| String::default());
290 result.insert(key.to_owned(), p_value);
291 }
292
293 }
304
305pub fn urlDecode(encoded: &str) -> Result<String> {
306 if let Ok(decoded) = decode(encoded) {
307 Ok(decoded.to_string())
308 } else {
309 Err(Exceptions::illegal_state_with(
310 "UnsupportedEncodingException",
311 ))
312 }
313}
314
315pub fn matchPrefixedField(
316 prefix: &str,
317 rawText: &str,
318 endChar: char,
319 trim: bool,
320) -> Option<Vec<String>> {
321 let mut matches = Vec::new();
322 let mut i = 0;
323 let max = rawText.len();
324 while i < max {
325 i = if let Some(loc) = rawText[i..].find(prefix) {
326 loc + i
327 } else {
328 break;
329 };
330 i += prefix.chars().count(); let start = i; let mut more = true;
337 while more {
338 if let Some(next_index) = rawText[i..].find(endChar) {
339 i += next_index;
340 } else {
341 i = rawText.chars().count();
343 more = false;
344 continue;
345 }
346
347 if countPrecedingBackslashes(rawText, i) % 2 != 0 {
348 i += 1;
350 } else {
351 let mut element = unescapeBackslash(&rawText[start..i]);
353 if trim {
354 element = element.trim().to_owned();
355 }
356 if !element.is_empty() {
357 matches.push(element);
358 }
359 i += 1;
360 more = false;
361 }
362
363 }
384 }
385 if matches.is_empty() {
386 return None;
387 }
388
389 Some(matches)
390}
391
392pub fn countPrecedingBackslashes(s: &str, pos: usize) -> u32 {
393 let mut count = 0;
394 let cached_s = s.chars().collect::<Vec<_>>();
395 for i in (0..pos).rev() {
396 if cached_s[i] == '\\' {
398 count += 1;
399 } else {
400 break;
401 }
402 }
403 count
404}
405
406pub fn matchSinglePrefixedField(
407 prefix: &str,
408 rawText: &str,
409 endChar: char,
410 trim: bool,
411) -> Option<String> {
412 let matches = matchPrefixedField(prefix, rawText, endChar, trim);
413 matches.map(|m| m[0].clone())
414 }
416
417pub fn match_docomo_prefixed_field(prefix: &str, raw_text: &str) -> Option<Vec<String>> {
418 matchPrefixedField(prefix, raw_text, ';', true)
419}
420
421pub fn match_single_docomo_prefixed_field(
422 prefix: &str,
423 raw_text: &str,
424 trim: bool,
425) -> Option<String> {
426 matchSinglePrefixedField(prefix, raw_text, ';', trim)
427}
428
429#[cfg(test)]
430mod tests {
431 use crate::{
432 client::result::{
433 OtherParsedResult, ParsedClientResult, ParsedRXingResult, TextParsedRXingResult,
434 },
435 RXingResult,
436 };
437
438 use super::parse_result_with_parser;
439
440 #[test]
441 fn test_single_parser() {
442 let result: RXingResult = RXingResult::new(
443 "text",
444 vec![12, 23, 54, 23],
445 Vec::new(),
446 crate::BarcodeFormat::EAN_13,
447 );
448 let p_res = parse_result_with_parser(&result, |_| {
449 Some(ParsedClientResult::TextResult(TextParsedRXingResult::new(
450 String::from("parsed with parser"),
451 String::from("en/us"),
452 )))
453 })
454 .unwrap();
455 assert_eq!(p_res.to_string(), "parsed with parser");
456 }
457
458 #[test]
459 fn test_other_parser() {
460 let result: RXingResult = RXingResult::new(
461 "text",
462 vec![12, 23, 54, 23],
463 Vec::new(),
464 crate::BarcodeFormat::EAN_13,
465 );
466 let p_res = parse_result_with_parser(&result, |v| {
467 Some(ParsedClientResult::Other(OtherParsedResult::new(Box::new(
468 v.getRawBytes().to_vec(),
469 ))))
470 })
471 .unwrap();
472
473 assert_eq!(p_res.getDisplayRXingResult(), "Any { .. }");
474
475 if let ParsedClientResult::Other(opr) = p_res {
476 if let Some(d) = opr.get_data().downcast_ref::<Vec<u8>>() {
477 assert_eq!(d, result.getRawBytes());
478 } else {
479 panic!("did not get vec<u8>");
480 }
481 } else {
482 panic!("did not get ParsedClientResult::Other");
483 }
484 }
485}