use crate::types::http_request::MapUrl;
use crate::types::state::FullPath;
use globset::Glob;
use std::collections::HashMap;
use url::Url;
use urlencoding::decode;
pub fn map_url(url: &str) -> Result<MapUrl, &'static str> {
let parsed_url = build_url(url)?;
let requested_path = decode_path(&parsed_url)?;
let token = map_token(parsed_url.clone());
Ok(MapUrl {
path: requested_path,
token,
})
}
pub fn map_alternative_paths(path: &String) -> Vec<String> {
aliases_of(&path.to_string())
}
pub fn alternative_paths(full_path: &FullPath) -> Option<Vec<String>> {
let extensions: Vec<&str> = full_path.split('.').collect();
let extension = extensions.last().unwrap_or(&"").trim();
if extension != "html" {
return None;
}
if full_path == "/index.html" {
return Some(Vec::from(["/".to_string()]));
}
aliased_by(full_path)
}
fn aliases_of(key: &String) -> Vec<String> {
if key.ends_with('/') {
vec![format!("{}index.html", key)]
} else if !key.ends_with(".html") {
vec![format!("{}.html", key), format!("{}/index.html", key)]
} else {
Vec::new()
}
}
fn aliased_by(key: &String) -> Option<Vec<String>> {
if key == "/index.html" {
Some(vec![
key[..(key.len() - 5)].into(),
key[..(key.len() - 10)].into(),
])
} else if key.ends_with("/index.html") {
Some(vec![
key[..(key.len() - 5)].into(),
key[..(key.len() - 10)].into(),
key[..(key.len() - 11)].to_string(),
])
} else if key.ends_with(".html") {
Some(vec![key[..(key.len() - 5)].to_string()])
} else {
None
}
}
fn build_url(url: &str) -> Result<Url, &'static str> {
let separator = separator(url);
let parsed_url = Url::parse(&["http://localhost", separator, url].join(""));
match parsed_url {
Err(_) => {
let error = format!("Url {} cannot be parsed.", url.to_owned()).into_boxed_str();
Err(Box::leak(error))
}
Ok(url) => Ok(url),
}
}
fn decode_path(parsed_url: &Url) -> Result<String, &'static str> {
let path = parsed_url.path();
let decoded_path = decode(path);
match decoded_path {
Err(_) => {
let error = format!("Path {path} cannot be decoded.").into_boxed_str();
Err(Box::leak(error))
}
Ok(decoded_path) => Ok(decoded_path.to_string()),
}
}
pub fn separator(url: &str) -> &str {
if url.starts_with('/') {
""
} else {
"/"
}
}
fn map_token(parsed_url: Url) -> Option<String> {
let tokens: Vec<String> = parsed_url
.query_pairs()
.filter_map(|(name, value)| {
if name == "token" {
Some(value.into_owned())
} else {
None
}
})
.collect();
if !tokens.is_empty() {
let token = tokens[0].clone();
return Some(token);
}
None
}
pub fn matching_urls<T: Clone>(
requested_path: &str,
config: &HashMap<String, T>,
) -> Vec<(String, T)> {
config
.iter()
.filter(|(source, _)| {
let glob = Glob::new(source);
match glob {
Err(_) => false,
Ok(glob) => {
let matcher = glob.compile_matcher();
matcher.is_match(requested_path)
}
}
})
.map(|(source, destination)| (source.clone(), destination.clone()))
.collect()
}