use log::info;
use percent_encoding::percent_decode_str;
use reqwest::Url;
use std::{
collections::HashSet,
path::{Path, PathBuf},
};
use crate::{
helpers::{path, url},
types::{raw_uri::RawUri, InputContent, InputSource},
Base, ErrorKind, Request, Result, Uri,
};
const MAX_TRUNCATED_STR_LEN: usize = 100;
pub(crate) fn create(
uris: Vec<RawUri>,
input_content: &InputContent,
base: &Option<Base>,
) -> Result<HashSet<Request>> {
let base_url = Base::from_source(&input_content.source);
let requests: Result<Vec<Option<Request>>> = uris
.into_iter()
.map(|raw_uri| {
let is_anchor = raw_uri.is_anchor();
let text = raw_uri.text.clone();
let element = raw_uri.element.clone();
let attribute = raw_uri.attribute.clone();
let source = match &input_content.source {
InputSource::String(s) => {
InputSource::String(s.chars().take(MAX_TRUNCATED_STR_LEN).collect())
}
c => c.clone(),
};
if let Ok(uri) = Uri::try_from(raw_uri) {
Ok(Some(Request::new(uri, source, element, attribute)))
} else if let Some(url) = base.as_ref().and_then(|u| u.join(&text)) {
Ok(Some(Request::new(Uri { url }, source, element, attribute)))
} else if let InputSource::FsPath(root) = &input_content.source {
if is_anchor {
Ok(None)
} else if let Some(url) = create_uri_from_path(root, &text, base)? {
Ok(Some(Request::new(Uri { url }, source, element, attribute)))
} else {
Ok(None)
}
} else if let Some(url) = construct_url(&base_url, &text) {
if base.is_some() {
Ok(None)
} else {
Ok(Some(Request::new(
Uri { url: url? },
source,
element,
attribute,
)))
}
} else {
info!("Handling of `{}` not implemented yet", text);
Ok(None)
}
})
.collect();
let requests: Vec<Request> = requests?.into_iter().flatten().collect();
Ok(HashSet::from_iter(requests))
}
fn construct_url(base: &Option<Url>, text: &str) -> Option<Result<Url>> {
base.as_ref().map(|base| {
base.join(text)
.map_err(|e| ErrorKind::ParseUrl(e, format!("{base}{text}")))
})
}
fn create_uri_from_path(src: &Path, dst: &str, base: &Option<Base>) -> Result<Option<Url>> {
let dst = url::remove_get_params_and_fragment(dst);
let decoded = percent_decode_str(dst).decode_utf8()?;
let resolved = path::resolve(src, &PathBuf::from(&*decoded), base)?;
match resolved {
Some(path) => Url::from_file_path(&path)
.map(Some)
.map_err(|_e| ErrorKind::InvalidUrlFromPath(path)),
None => Ok(None),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_uri_from_path() {
let result =
create_uri_from_path(&PathBuf::from("/README.md"), "test+encoding", &None).unwrap();
assert_eq!(result.unwrap().as_str(), "file:///test+encoding");
}
}