use std::char;
pub(crate) fn find_authority_end(
s: &str,
mut userinfo_allowed: bool,
require_host: bool,
port_allowed: bool,
iri_parsing_enabled: bool,
) -> (Option<usize>, Option<usize>) {
let mut end = Some(0);
let mut maybe_last_dot = None;
let mut last_dot = None;
let mut number_dots = 0;
let mut dot_allowed = false;
let mut hyphen_allowed = false;
let mut all_numeric = true;
let mut maybe_host = true;
let mut host_ended = false;
for (i, c) in s.char_indices() {
let can_be_last = match c {
'a'..='z' | 'A'..='Z' | '\u{80}'..=char::MAX => {
if !iri_parsing_enabled && c > '\u{80}' {
break;
}
dot_allowed = true;
hyphen_allowed = true;
last_dot = maybe_last_dot;
all_numeric = false;
if host_ended {
maybe_host = false;
}
!require_host || !host_ended
}
'0'..='9' => {
dot_allowed = true;
hyphen_allowed = true;
if last_dot != maybe_last_dot {
last_dot = maybe_last_dot;
number_dots += 1;
}
if host_ended {
maybe_host = false;
}
!require_host || !host_ended
}
'-' => {
if !hyphen_allowed {
maybe_host = false;
}
dot_allowed = false;
all_numeric = false;
!require_host
}
'.' => {
if !dot_allowed {
host_ended = true;
}
dot_allowed = false;
hyphen_allowed = false;
maybe_last_dot = Some(i);
false
}
'_' | '~' => {
maybe_host = false;
false
}
'!' | '$' | '&' | '\'' | '(' | ')' | '*' | '+' | ',' | ';' | '=' => {
host_ended = true;
if !userinfo_allowed && require_host {
break;
}
false
}
':' => {
if !userinfo_allowed && !port_allowed {
break;
}
maybe_last_dot = last_dot;
false
}
'@' => {
if !userinfo_allowed {
return (None, None);
}
userinfo_allowed = false;
maybe_last_dot = None;
last_dot = None;
dot_allowed = false;
hyphen_allowed = false;
all_numeric = true;
maybe_host = true;
host_ended = false;
false
}
'/' => {
if !require_host {
end = Some(i);
}
break;
}
_ => {
break;
}
};
if can_be_last {
end = Some(i + c.len_utf8());
}
}
if require_host {
if maybe_host {
if all_numeric {
if number_dots != 3 {
return (None, None);
}
} else {
if let Some(last_dot) = last_dot {
if !valid_tld(&s[last_dot + 1..]) {
return (None, None);
}
}
}
(end, last_dot)
} else {
(None, None)
}
} else {
(end, last_dot)
}
}
fn valid_tld(tld: &str) -> bool {
tld.chars()
.take_while(|c| c.is_ascii_alphabetic())
.take(2)
.count()
>= 2
}