use super::encoding::url;
#[derive(Debug, PartialEq, Eq)]
pub struct Path {
pub raw: String,
pub parts: Vec<PathPart>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum PathPart {
Normal(String),
Param(String),
AnyAfter,
Any,
}
impl Path {
pub fn new(path: String) -> Path {
let path = normalize_path(path);
let mut out = Vec::new();
for i in path.split('/') {
out.push(PathPart::from_segment(i));
}
Path {
raw: path,
parts: out,
}
}
pub fn match_path(&self, path: String) -> Option<Vec<(String, String)>> {
if self.parts == [PathPart::AnyAfter] {
return Some(Vec::new());
}
let path = normalize_path(path);
let mut out = Vec::new();
let path = path.split('/');
for (i, j) in self.parts.iter().zip(path.clone()) {
match i {
PathPart::Normal(x) => {
if x != j {
return None;
}
}
PathPart::Param(x) => {
out.push((x.to_owned(), url::decode(j).unwrap_or_else(|| j.to_owned())))
}
PathPart::AnyAfter => return Some(out),
PathPart::Any => {}
}
}
if path.count() != self.parts.len() {
return None;
}
Some(out)
}
}
impl PathPart {
pub fn from_segment(seg: &str) -> PathPart {
match seg {
"*" => PathPart::Any,
"**" => PathPart::AnyAfter,
x if x.starts_with('{') && x.ends_with('}') => PathPart::Param(
x.strip_prefix('{')
.unwrap()
.strip_suffix('}')
.unwrap()
.to_owned(),
),
_ => PathPart::Normal(seg.to_owned()),
}
}
}
pub fn normalize_path(mut path: String) -> String {
while path.ends_with('/') {
path.pop();
}
while path.starts_with('/') {
path.remove(0);
}
path
}
#[cfg(test)]
mod test {
use super::{normalize_path, Path, PathPart};
#[test]
fn test_path_new() {
assert_eq!(
Path::new("/".to_owned()),
Path {
parts: vec![PathPart::Normal("".to_owned())],
raw: "".to_owned()
}
);
assert_eq!(
Path::new("/cool/{bean}".to_owned()),
Path {
parts: vec![
PathPart::Normal("cool".to_owned()),
PathPart::Param("bean".to_owned())
],
raw: "cool/{bean}".to_owned()
}
);
assert_eq!(
Path::new("idk/*".to_owned()),
Path {
parts: vec![PathPart::Normal("idk".to_owned()), PathPart::Any],
raw: "idk/*".to_owned()
}
)
}
#[test]
fn test_match_path_normal() {
assert_eq!(
Path::new("/".to_owned()).match_path("/".to_owned()),
Some(vec![])
);
assert_eq!(
Path::new("/".to_owned()).match_path("".to_owned()),
Some(vec![])
);
}
#[test]
fn test_match_path_param() {
assert_eq!(
Path::new("/cool/{bean}".to_owned()).match_path("/Cool/Bean".to_owned()),
None
);
assert_eq!(
Path::new("/cool/{bean}".to_owned()).match_path("/cool/Bean".to_owned()),
Some(vec![("bean".to_owned(), "Bean".to_owned())])
);
}
#[test]
fn test_match_path_any() {
assert_eq!(
Path::new("idk/*".to_owned()).match_path("/idk/Cool Beans".to_owned()),
Some(vec![])
);
assert_eq!(
Path::new("idk/*".to_owned()).match_path("/idk/Cool/Beans".to_owned()),
None
);
}
#[test]
fn test_path_part_from_normal() {
assert_eq!(
PathPart::from_segment("coolbeans"),
PathPart::Normal("coolbeans".to_owned())
);
assert_eq!(PathPart::from_segment(""), PathPart::Normal("".to_owned()));
}
#[test]
fn test_path_part_from_param() {
assert_eq!(
PathPart::from_segment("{bean}"),
PathPart::Param("bean".to_owned())
);
assert_eq!(PathPart::from_segment("{}"), PathPart::Param("".to_owned()));
}
#[test]
fn test_path_part_from_any() {
assert_eq!(PathPart::from_segment("*"), PathPart::Any);
}
#[test]
fn test_normalize_path() {
assert_eq!(
normalize_path("/COOL/BEANS/".to_owned()),
"COOL/BEANS".to_owned()
);
assert_eq!(
normalize_path("////COOL/BEANS////".to_owned()),
"COOL/BEANS".to_owned()
);
}
}