use std::path::PathBuf;
use crate::RawStr;
use crate::uri::fmt::{Part, Path, Query};
use crate::uri::error::PathError;
#[derive(Debug, Clone)]
pub struct Segments<'a, P: Part> {
pub(super) source: &'a RawStr,
pub(super) segments: &'a [P::Raw],
pub(super) pos: usize,
}
impl<P: Part> Segments<'_, P> {
#[doc(hidden)]
#[inline(always)]
pub fn new<'a>(source: &'a RawStr, segments: &'a [P::Raw]) -> Segments<'a, P> {
Segments { source, segments, pos: 0, }
}
#[inline]
pub fn len(&self) -> usize {
let max_pos = std::cmp::min(self.pos, self.segments.len());
self.segments.len() - max_pos
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn skip(mut self, n: usize) -> Self {
self.pos = std::cmp::min(self.pos + n, self.segments.len());
self
}
}
impl<'a> Segments<'a, Path> {
#[inline]
pub fn get(&self, n: usize) -> Option<&'a str> {
self.segments.get(self.pos + n)
.map(|i| i.from_source(Some(self.source.as_str())))
}
#[inline]
pub fn prefix_of(self, other: Segments<'_, Path>) -> bool {
if self.len() > other.len() {
return false;
}
self.zip(other).all(|(a, b)| a == b)
}
pub fn to_path_buf(&self, allow_dotfiles: bool) -> Result<PathBuf, PathError> {
let mut buf = PathBuf::new();
for segment in self.clone() {
if segment == ".." {
buf.pop();
} else if !allow_dotfiles && segment.starts_with('.') {
return Err(PathError::BadStart('.'))
} else if segment.starts_with('*') {
return Err(PathError::BadStart('*'))
} else if segment.ends_with(':') {
return Err(PathError::BadEnd(':'))
} else if segment.ends_with('>') {
return Err(PathError::BadEnd('>'))
} else if segment.ends_with('<') {
return Err(PathError::BadEnd('<'))
} else if segment.contains('/') {
return Err(PathError::BadChar('/'))
} else if cfg!(windows) && segment.contains('\\') {
return Err(PathError::BadChar('\\'))
} else if cfg!(windows) && segment.contains(':') {
return Err(PathError::BadChar(':'))
} else {
buf.push(&*segment)
}
}
Ok(buf)
}
}
impl<'a> Segments<'a, Query> {
#[inline]
pub fn get(&self, n: usize) -> Option<(&'a str, &'a str)> {
let (name, val) = self.segments.get(self.pos + n)?;
let source = Some(self.source.as_str());
let name = name.from_source(source);
let val = val.from_source(source);
Some((name, val))
}
}
macro_rules! impl_iterator {
($T:ty => $I:ty) => (
impl<'a> Iterator for Segments<'a, $T> {
type Item = $I;
fn next(&mut self) -> Option<Self::Item> {
let item = self.get(0)?;
self.pos += 1;
Some(item)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len(), Some(self.len()))
}
fn count(self) -> usize {
self.len()
}
}
)
}
impl_iterator!(Path => &'a str);
impl_iterator!(Query => (&'a str, &'a str));