use std::ops::Range;
use crate::domains::find_authority_end;
use crate::scanner::Scanner;
pub struct EmailScanner {
pub domain_must_have_dot: bool,
}
impl Scanner for EmailScanner {
fn scan(&self, s: &str, at: usize) -> Option<Range<usize>> {
if let Some(start) = self.find_start(&s[0..at]) {
let after = at + 1;
if let Some(end) = self.find_end(&s[after..]) {
let range = Range {
start,
end: after + end,
};
return Some(range);
}
}
None
}
}
impl EmailScanner {
fn find_start(&self, s: &str) -> Option<usize> {
let mut first = None;
let mut atom_boundary = true;
for (i, c) in s.char_indices().rev() {
if Self::local_atom_allowed(c) {
first = Some(i);
atom_boundary = false;
} else if c == '.' {
if atom_boundary {
break;
}
atom_boundary = true;
} else if c == '@' {
return None;
} else {
break;
}
}
first
}
fn find_end(&self, s: &str) -> Option<usize> {
if let (Some(end), last_dot) = find_authority_end(s, false, true, false, true) {
if !self.domain_must_have_dot || last_dot.is_some() {
Some(end)
} else {
None
}
} else {
None
}
}
fn local_atom_allowed(c: char) -> bool {
match c {
'a'..='z'
| 'A'..='Z'
| '0'..='9'
| '!'
| '#'
| '$'
| '%'
| '&'
| '\''
| '*'
| '+'
| '-'
| '/'
| '='
| '?'
| '^'
| '_'
| '`'
| '{'
| '|'
| '}'
| '~' => true,
_ => c >= '\u{80}',
}
}
}