49 lines
1.4 KiB
Rust
49 lines
1.4 KiB
Rust
use std::borrow::Cow;
|
|
use std::fmt;
|
|
|
|
pub trait PathFormatter {
|
|
fn append(&mut self, component: &str) -> fmt::Result;
|
|
}
|
|
|
|
pub trait Path: Default + fmt::Display + Clone {
|
|
fn fmt(&self, f: &mut dyn PathFormatter) -> fmt::Result;
|
|
fn parse(path: &str) -> Self;
|
|
fn crumbs<F: FnMut(Self)>(&self, mut add: F) {
|
|
add(self.clone());
|
|
}
|
|
}
|
|
|
|
pub fn next_component(path: &str) -> (Cow<'_, str>, Option<&str>) {
|
|
let (next, rem) = match path.find('/') {
|
|
None => (path, None),
|
|
Some(sep) => (&path[..sep], Some(&path[sep+1..])),
|
|
};
|
|
(percent_encoding::percent_decode_str(next).decode_utf8_lossy(), rem)
|
|
}
|
|
|
|
// fragment specials plus directory separator
|
|
const FRAGMENT_DIR: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`').add(b'/').add(b'%');
|
|
struct PathString(String);
|
|
impl PathFormatter for PathString {
|
|
fn append(&mut self, component: &str) -> fmt::Result {
|
|
use std::fmt::Write;
|
|
if self.0.is_empty() {
|
|
write!(&mut self.0, "{}", percent_encoding::utf8_percent_encode(component, FRAGMENT_DIR))
|
|
} else {
|
|
write!(&mut self.0, "/{}", percent_encoding::utf8_percent_encode(component, FRAGMENT_DIR))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn make_path<P: Path>(path: &P) -> Result<String, fmt::Error> {
|
|
let mut p = PathString(String::new());
|
|
Path::fmt(path, &mut p)?;
|
|
Ok(p.0)
|
|
}
|
|
|
|
pub fn crumbs<P: Path>(path: &P) -> Vec<P> {
|
|
let mut crumbs = Vec::new();
|
|
path.crumbs(|crumb| crumbs.push(crumb));
|
|
crumbs
|
|
}
|