use super::{Error, ErrorKind};
use alloc::borrow::Cow;
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
pub const MAX_OCTETS: usize = 4096;
pub const MAX_SEGMENTS: usize = 64;
pub const MAX_SELECTED: usize = 65_536;
pub fn segments(path: &str) -> Result<Vec<String>, Error> {
if path.len() > MAX_OCTETS {
let reason = format!("a path has {MAX_OCTETS} octets at most");
let start = path
.char_indices()
.nth(64)
.map_or(path, |(end, _)| &path[..end]);
return Err(Error::new(ErrorKind::Limit, start, reason));
}
if path.is_empty() {
return Ok(Vec::new());
}
let Some(rest) = path.strip_prefix('/') else {
let reason = format!("{path:?} is no path: a path starts with \"/\"");
return Err(Error::new(ErrorKind::Path, path, reason));
};
let mut parts = Vec::new();
for part in rest.split('/') {
if parts.len() == MAX_SEGMENTS {
let reason = format!("a path has {MAX_SEGMENTS} segments at most");
return Err(Error::new(ErrorKind::Limit, path, reason));
}
let mut decoded = String::with_capacity(part.len());
let mut characters = part.chars();
while let Some(character) = characters.next() {
decoded.push(match (character, character == '~') {
(plain, false) => plain,
(_, true) => match characters.next() {
Some('0') => '~',
Some('1') => '/',
_ => {
let reason = "in a path, \"~\" is followed by 0 or 1, which stand for \
\"~\" and \"/\"";
return Err(Error::new(ErrorKind::Path, path, reason));
}
},
});
}
parts.push(decoded);
}
Ok(parts)
}
pub fn position(segment: &str) -> Option<usize> {
let plain = !segment.is_empty()
&& segment.bytes().all(|digit| digit.is_ascii_digit())
&& (segment == "0" || !segment.starts_with('0'));
segment.parse().ok().filter(|_| plain)
}
pub fn escape(segment: &str) -> Cow<'_, str> {
match segment.contains(['~', '/']) {
true => Cow::Owned(segment.replace('~', "~0").replace('/', "~1")),
false => Cow::Borrowed(segment),
}
}