use simpath::{FoundType, Simpath};
use url::Url;
use crate::content::file_provider::FileProvider;
use crate::content::http_provider::HttpProvider;
use crate::errors::*;
pub trait Provider {
fn resolve_url(&self, url: &Url, default_file: &str, extensions: &[&str]) -> Result<Url>;
fn get_contents(&self, url: &Url) -> Result<Vec<u8>>;
}
pub trait LibProvider {
fn resolve_url(
&self,
url: &Url,
default_file: &str,
extensions: &[&str],
) -> Result<(Url, Option<String>)>;
fn get_contents(&self, url: &Url) -> Result<Vec<u8>>;
}
const FILE_PROVIDER: &dyn Provider = &FileProvider as &dyn Provider;
const HTTP_PROVIDER: &dyn Provider = &HttpProvider as &dyn Provider;
pub struct MetaProvider {
lib_search_path: Simpath,
}
impl MetaProvider {
pub fn new(lib_search_path: Simpath) -> Self {
MetaProvider { lib_search_path }
}
fn get_provider(&self, scheme: &str) -> Result<&dyn Provider> {
match scheme {
"file" => Ok(FILE_PROVIDER),
"http" | "https" => Ok(HTTP_PROVIDER),
_ => bail!(
"Cannot determine which provider to use for url with scheme: '{}'",
scheme
),
}
}
fn resolve_lib_url(&self, url: &Url) -> Result<(Url, Option<String>)> {
let lib_name = url
.host_str()
.chain_err(|| format!("'lib_name' could not be extracted from the url '{}'", url))?;
let path_under_lib = url.path().trim_start_matches('/');
let lib_reference = Some(format!("{}/{}", lib_name, path_under_lib));
match self.lib_search_path.find(lib_name) {
Ok(FoundType::File(lib_root_path)) => {
let lib_path = lib_root_path.join(path_under_lib);
Ok((
Url::from_directory_path(lib_path)
.map_err(|_| "Could not convert file: lib_path to Url")?,
lib_reference,
))
}
Ok(FoundType::Resource(mut lib_root_url)) => {
lib_root_url.set_path(&format!("{}/{}", lib_root_url.path(), path_under_lib));
Ok((lib_root_url, lib_reference))
}
_ => bail!(
"Could not resolve library Url '{}' using library search path",
url
),
}
}
}
impl LibProvider for MetaProvider {
fn resolve_url(
&self,
url: &Url,
default_filename: &str,
extensions: &[&str],
) -> Result<(Url, Option<String>)> {
let (content_url, lib_reference) = if url.scheme() == "lib" {
self.resolve_lib_url(url)?
} else {
(url.clone(), None)
};
let provider = self.get_provider(&content_url.scheme())?;
let resolved_url = provider.resolve_url(&content_url, default_filename, extensions)?;
Ok((resolved_url, lib_reference))
}
fn get_contents(&self, url: &Url) -> Result<Vec<u8>> {
let scheme = url.scheme().to_string();
let provider = self.get_provider(&scheme)?;
let content = provider.get_contents(&url)?;
Ok(content)
}
}
#[cfg(test)]
mod test {
use std::path::Path;
use simpath::Simpath;
use url::Url;
use super::{LibProvider, MetaProvider};
#[test]
fn get_invalid_provider() {
let search_path = Simpath::new("TEST");
let meta = MetaProvider::new(search_path);
assert!(meta.get_provider("fake://bla").is_err());
}
#[test]
fn get_http_provider() {
let search_path = Simpath::new("TEST");
let meta = MetaProvider::new(search_path);
assert!(meta.get_provider("http").is_ok());
}
#[test]
fn get_https_provider() {
let search_path = Simpath::new("TEST");
let meta = MetaProvider::new(search_path);
assert!(meta.get_provider("https").is_ok());
}
#[test]
fn get_file_provider() {
let search_path = Simpath::new("TEST");
let meta = MetaProvider::new(search_path);
assert!(meta.get_provider("file").is_ok());
}
fn set_lib_search_path() -> Simpath {
let mut lib_search_path = Simpath::new("lib_search_path");
let root_str = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Could not get project root dir");
lib_search_path.add_directory(
root_str
.to_str()
.expect("Could not get root path as string"),
);
println!("{}", lib_search_path);
lib_search_path
}
#[test]
fn resolve_path() {
let root_str = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("Could not get project root dir");
let expected_url = Url::parse(&format!(
"file://{}/flowstdlib/control/tap/tap.toml",
root_str.display().to_string()
))
.expect("Could not create expected url");
let provider = &MetaProvider::new(set_lib_search_path()) as &dyn LibProvider;
let lib_url = Url::parse("lib://flowstdlib/control/tap").expect("Couldn't form Url");
match provider.resolve_url(&lib_url, "", &["toml"]) {
Ok((url, lib_ref)) => {
assert_eq!(url, expected_url);
assert_eq!(lib_ref, Some("flowstdlib/control/tap".to_string()));
}
Err(_) => panic!("Error trying to resolve url"),
}
}
#[test]
fn resolve_web_path() {
let mut search_path = Simpath::new("web_path");
search_path.add_url(
&Url::parse(
"https://raw.githubusercontent.com/andrewdavidmackenzie/flow/master/flowstdlib",
)
.expect("Could not parse the url for Simpath"),
);
let expected_url = Url::parse("https://raw.githubusercontent.com/andrewdavidmackenzie/flow/master/flowstdlib/control/tap/tap.toml")
.expect("Couldn't parse expected Url");
let provider = &MetaProvider::new(search_path);
let lib_url = Url::parse("lib://flowstdlib/control/tap").expect("Couldn't create Url");
let (resolved_url, _) = provider
.resolve_url(&lib_url, "", &["toml"])
.expect("Couldn't resolve library on the web");
assert_eq!(resolved_url, expected_url);
}
}