#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) struct AuthorityUrl<'a> {
pub(crate) prefix: &'a str, pub(crate) authority: &'a str, pub(crate) suffix: &'a str, }
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub(crate) struct MailtoUrl<'a> {
pub(crate) body: &'a str,
pub(crate) address: &'a str,
pub(crate) query: &'a str,
}
pub(crate) fn split_authority_url(input: &str) -> Option<AuthorityUrl<'_>> {
let authority_start = if input.starts_with("//") {
2
} else {
let scheme_end = input.find("://")?;
let scheme = &input[..scheme_end];
if !is_url_scheme(scheme) {
return None;
}
scheme_end + 3
};
let (authority, suffix) = split_authority_suffix(&input[authority_start..]);
Some(AuthorityUrl {
prefix: &input[..authority_start],
authority,
suffix,
})
}
pub(crate) fn split_authority_suffix(input: &str) -> (&str, &str) {
let authority_end = input.find(['/', '?', '#']).unwrap_or(input.len());
(&input[..authority_end], &input[authority_end..])
}
pub(crate) fn split_mailto_url(input: &str) -> Option<MailtoUrl<'_>> {
let body = strip_ascii_prefix(input, "mailto:")?;
let query_start = body.find('?').unwrap_or(body.len());
Some(MailtoUrl {
body,
address: &body[..query_start],
query: &body[query_start..],
})
}
pub(crate) fn strip_ascii_prefix<'a>(input: &'a str, prefix: &str) -> Option<&'a str> {
if input
.get(..prefix.len())
.is_some_and(|actual| actual.eq_ignore_ascii_case(prefix))
{
Some(&input[prefix.len()..])
} else {
None
}
}
pub(crate) fn is_url_scheme(input: &str) -> bool {
let mut bytes = input.bytes();
if let Some(first) = bytes.next() {
first.is_ascii_alphabetic()
&& bytes.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'-' | b'.'))
} else {
false
}
}