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(&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(path: &P) -> Result { let mut p = PathString(String::new()); Path::fmt(path, &mut p)?; Ok(p.0) } pub fn crumbs(path: &P) -> Vec

{ let mut crumbs = Vec::new(); path.crumbs(|crumb| crumbs.push(crumb)); crumbs }