#![deny(warnings)]
pub enum UrlPath {
Path {
parent: Option<String>,
last: Option<String>,
is_absolute: bool,
},
External(String),
}
impl UrlPath {
pub fn new(path: &str) -> Self {
let (parent, last) = Self::canonicalize(path);
let is_external = path.starts_with("http:") || path.starts_with("https:");
let is_absolute = path.starts_with("/");
if is_external {
UrlPath::External(path.to_string())
} else {
UrlPath::Path {
parent,
last,
is_absolute,
}
}
}
pub fn is_absolute(&self) -> bool {
match self {
UrlPath::Path {
ref is_absolute, ..
} => *is_absolute,
UrlPath::External(_) => false,
}
}
pub fn is_external(&self) -> bool {
match self {
UrlPath::External(_) => true,
UrlPath::Path { .. } => false,
}
}
fn canonicalize(path: &str) -> (Option<String>, Option<String>) {
let segments: Vec<&str> = path.split("/").collect();
let mut path: Vec<String> = vec![];
let segments2: Vec<&str> = segments
.into_iter()
.filter(|s| !(s.is_empty() || *s == "."))
.collect();
let _filtered: Vec<&str> = segments2
.into_iter()
.inspect(|s| {
if *s == ".." {
path.pop();
} else {
path.push(s.to_string())
}
})
.collect();
let filename = path.pop();
let parent = path.join("/");
let parent = if parent.is_empty() {
None
} else {
Some(parent)
};
(parent, filename)
}
pub fn last(&self) -> Option<String> {
match self {
UrlPath::Path { last, .. } => last.clone(),
UrlPath::External(_) => None,
}
}
pub fn parent(&self) -> Option<String> {
match self {
UrlPath::Path { parent, .. } => parent.clone(),
UrlPath::External(_) => None,
}
}
pub fn extension(&self) -> Option<String> {
if let Some(last) = self.last() {
let splinters: Vec<&str> = last.split(".").collect();
if splinters.len() > 0 {
let last = splinters[splinters.len() - 1];
Some(last.to_string())
} else {
None
}
} else {
None
}
}
pub fn is_extension(&self, ext: &str) -> bool {
if let Some(ex) = self.extension() {
ex == ext
} else {
false
}
}
pub fn normalize(&self) -> String {
match self {
UrlPath::Path {
parent,
last,
is_absolute,
} => {
let full_path = if let Some(ref parent) = parent {
if let Some(ref file) = last {
format!("{}/{}", parent, file)
} else {
format!("{}", parent)
}
} else if let Some(ref file) = last {
if let Some(ref parent) = parent {
format!("{}/{}", parent, file)
} else {
format!("{}", file)
}
} else {
"".to_string()
};
if *is_absolute {
format!("/{}", full_path)
} else {
full_path
}
}
UrlPath::External(ref s) => s.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let url = "md/README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "md/README.md";
assert_eq!(expected, result);
}
#[test]
fn relative_file() {
let url = "./README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "README.md";
assert_eq!(expected, result);
}
#[test]
fn external_link() {
let url = "https://raw.githubusercontent.com/ivanceras/svgbob/master/TODO.md";
let path = UrlPath::new(url);
assert!(path.is_external());
let result = path.normalize();
let expected = "https://raw.githubusercontent.com/ivanceras/svgbob/master/TODO.md";
assert_eq!(expected, result);
}
#[test]
fn absolute() {
let url = "/home/user/md/README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "/home/user/md/README.md";
assert_eq!(expected, result);
}
#[test]
fn absolute_with_dotdot() {
let url = "/home/user/md/../../README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "/home/README.md";
assert_eq!(expected, result);
}
#[test]
fn normalize1() {
let url = "md/more/../README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "md/README.md";
assert_eq!(expected, result);
}
#[test]
fn normalize2() {
let url = "md/../README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "README.md";
assert_eq!(expected, result);
}
#[test]
fn no_parent() {
let url = "README.md";
let path = UrlPath::new(url);
let last = "README.md";
assert_eq!(Some(last.to_string()), path.last());
assert_eq!(None, path.parent());
}
#[test]
fn no_parent2() {
let url = "md/../README.md";
let path = UrlPath::new(url);
let last = "README.md";
assert_eq!(Some(last.to_string()), path.last());
assert_eq!(None, path.parent());
}
#[test]
fn normalize_no_more_back() {
let url = "../../README.md";
let path = UrlPath::new(url);
let result = path.normalize();
let expected = "README.md";
assert_eq!(expected, result);
}
#[test]
fn md_extension() {
let url = "../../README.md";
let path = UrlPath::new(url);
let result = path.extension();
let expected = Some("md".to_string());
assert_eq!(expected, result);
}
#[test]
fn html_extension() {
let url = "index.html";
let path = UrlPath::new(url);
let result = path.extension();
let expected = Some("html".to_string());
assert_eq!(expected, result);
}
}