use std::path::PathBuf;
use std::str::Utf8Error;
use uri::Uri;
#[derive(Clone, Debug)]
pub struct Segments<'a>(pub &'a str);
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum SegmentError {
Utf8(Utf8Error),
BadStart(char),
BadChar(char),
BadEnd(char),
}
impl<'a> Segments<'a> {
pub fn into_path_buf(self, allow_dotfiles: bool) -> Result<PathBuf, SegmentError> {
let mut buf = PathBuf::new();
for segment in self {
let decoded = Uri::percent_decode(segment.as_bytes())
.map_err(SegmentError::Utf8)?;
if decoded == ".." {
buf.pop();
} else if !allow_dotfiles && decoded.starts_with('.') {
return Err(SegmentError::BadStart('.'))
} else if decoded.starts_with('*') {
return Err(SegmentError::BadStart('*'))
} else if decoded.ends_with(':') {
return Err(SegmentError::BadEnd(':'))
} else if decoded.ends_with('>') {
return Err(SegmentError::BadEnd('>'))
} else if decoded.ends_with('<') {
return Err(SegmentError::BadEnd('<'))
} else if decoded.contains('/') {
return Err(SegmentError::BadChar('/'))
} else if cfg!(windows) && decoded.contains('\\') {
return Err(SegmentError::BadChar('\\'))
} else {
buf.push(&*decoded)
}
}
Ok(buf)
}
}
impl<'a> Iterator for Segments<'a> {
type Item = &'a str;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let i = self.0.find(|c| c != '/')?;
let j = self.0[i..].find('/').map_or(self.0.len(), |j| i + j);
let result = Some(&self.0[i..j]);
self.0 = &self.0[j..];
result
}
}