#[allow(dead_code)]
pub(crate) struct UriComponents<'a> {
pub scheme_with_colon: Option<&'a str>,
pub scheme: Option<&'a str>,
pub authority_with_slashes: Option<&'a str>,
pub authority: Option<&'a str>,
pub path: &'a str,
pub query_with_marker: Option<&'a str>,
pub query: Option<&'a str>,
pub fragment_with_marker: Option<&'a str>,
pub fragment: Option<&'a str>,
pub scheme_start: usize,
pub scheme_end: usize,
}
pub(crate) fn parse_uri_components(uri: &str) -> Option<UriComponents<'_>> {
let bytes = uri.as_bytes();
let len = bytes.len();
let mut pos = 0;
let (scheme_with_colon, scheme, scheme_start, scheme_end) = {
let start = pos;
let mut found_scheme = false;
let mut colon_pos = 0;
let mut i = start;
while i < len {
match bytes[i] {
b':' => {
if i > start {
colon_pos = i;
found_scheme = true;
}
break;
}
b'/' | b'?' | b'#' | b'%' => break,
_ => i += 1,
}
}
if found_scheme {
let scheme_str = &uri[start..colon_pos];
let scheme_with_colon_str = &uri[start..=colon_pos];
pos = colon_pos + 1;
(
Some(scheme_with_colon_str),
Some(scheme_str),
start,
colon_pos,
)
} else {
(None, None, 0, 0)
}
};
let (authority_with_slashes, authority) = if pos + 1 < len
&& bytes[pos] == b'/'
&& bytes[pos + 1] == b'/'
{
let auth_start = pos + 2;
let mut auth_end = auth_start;
while auth_end < len && !matches!(bytes[auth_end], b'/' | b'?' | b'#') {
auth_end += 1;
}
let with_slashes = &uri[pos..auth_end];
let auth_only = &uri[auth_start..auth_end];
pos = auth_end;
(Some(with_slashes), Some(auth_only))
} else {
(None, None)
};
let path_start = pos;
while pos < len && !matches!(bytes[pos], b'?' | b'#') {
pos += 1;
}
let path = &uri[path_start..pos];
let (query_with_marker, query) = if pos < len && bytes[pos] == b'?' {
let q_start = pos;
pos += 1;
let q_val_start = pos;
while pos < len && bytes[pos] != b'#' {
pos += 1;
}
(Some(&uri[q_start..pos]), Some(&uri[q_val_start..pos]))
} else {
(None, None)
};
let (fragment_with_marker, fragment) = if pos < len && bytes[pos] == b'#' {
(Some(&uri[pos..]), Some(&uri[pos + 1..]))
} else {
(None, None)
};
Some(UriComponents {
scheme_with_colon,
scheme,
authority_with_slashes,
authority,
path,
query_with_marker,
query,
fragment_with_marker,
fragment,
scheme_start,
scheme_end,
})
}
pub(crate) fn is_valid_scheme(scheme: &str) -> bool {
let bytes = scheme.as_bytes();
if bytes.is_empty() {
return false;
}
if !bytes[0].is_ascii_alphabetic() {
return false;
}
bytes[1..]
.iter()
.all(|b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'+' | b'.'))
}
#[allow(dead_code)]
pub(crate) struct AuthorityComponents<'a> {
pub userinfo_with_at: Option<&'a str>,
pub userinfo: Option<&'a str>,
pub host: Option<&'a str>,
pub port_with_colon: Option<&'a str>,
pub port: Option<&'a str>,
}
pub(crate) fn parse_authority_components(authority: &str) -> Option<AuthorityComponents<'_>> {
let bytes = authority.as_bytes();
let len = bytes.len();
if len == 0 {
return None;
}
let at_pos = {
let mut found = None;
let mut i = 0;
while i < len {
match bytes[i] {
b'@' => {
found = Some(i);
break;
}
b'/' | b'?' | b'#' => break,
_ => i += 1,
}
}
found
};
let (userinfo_with_at, userinfo, host_port_start) = if let Some(at) = at_pos {
if at == 0 {
return None; }
(
Some(&authority[..=at]),
Some(&authority[..at]),
at + 1,
)
} else {
(None, None, 0)
};
let remaining = &authority[host_port_start..];
let rem_bytes = remaining.as_bytes();
let rem_len = rem_bytes.len();
if rem_len == 0 {
return None;
}
let (host, port_start) = if rem_bytes[0] == b'[' {
let close = rem_bytes.iter().position(|&b| b == b']')?;
let host_str = &remaining[..=close];
(Some(host_str), close + 1)
} else {
let mut end = 0;
while end < rem_len && rem_bytes[end] != b':' {
if rem_bytes[end] == b'[' || rem_bytes[end] == b']' {
return None; }
end += 1;
}
if end == 0 {
return None; }
(Some(&remaining[..end]), end)
};
let (port_with_colon, port) = if port_start < rem_len && rem_bytes[port_start] == b':' {
let port_str = &remaining[port_start + 1..];
if port_str.is_empty() || !port_str.bytes().all(|b| b.is_ascii_digit()) {
return None;
}
(
Some(&remaining[port_start..]),
Some(port_str),
)
} else if port_start < rem_len {
return None;
} else {
(None, None)
};
Some(AuthorityComponents {
userinfo_with_at,
userinfo,
host,
port_with_colon,
port,
})
}