pub mod error;
mod cache;
mod combined;
mod file;
mod file_cache;
mod noop;
mod result;
mod traits;
#[cfg(feature = "tokio")]
mod async_default;
#[cfg(feature = "tokio")]
mod async_file;
#[cfg(feature = "tokio")]
mod reqwest;
#[cfg(feature = "ureq")]
mod ureq;
#[cfg(feature = "tokio")]
pub use cache::AsyncCachingFetcher;
pub use cache::CachingFetcher;
pub use combined::CombinedFetcher;
pub use file::FileFetcher;
pub use file_cache::FileCachingFetcher;
pub use noop::NoopFetcher;
pub use result::FetchResult;
pub use traits::SchemaFetcher;
#[cfg(feature = "tokio")]
pub use file_cache::AsyncFileCachingFetcher;
#[cfg(feature = "tokio")]
pub use traits::AsyncSchemaFetcher;
#[cfg(feature = "tokio")]
pub use self::async_default::AsyncDefaultFetcher;
#[cfg(feature = "tokio")]
pub use self::async_file::AsyncFileFetcher;
#[cfg(feature = "tokio")]
pub use self::reqwest::ReqwestFetcher;
#[cfg(feature = "ureq")]
pub use self::ureq::UreqFetcher;
use dashmap::DashMap;
use std::path::Path;
pub struct DefaultFetcher {
inner: CombinedFetcher,
cache: DashMap<String, FetchResult>,
}
impl DefaultFetcher {
pub fn new() -> Self {
Self::with_base_dir_option(None)
}
pub fn with_base_dir(base_dir: impl AsRef<Path>) -> Self {
Self::with_base_dir_option(Some(base_dir.as_ref().to_path_buf()))
}
fn with_base_dir_option(base_dir: Option<std::path::PathBuf>) -> Self {
let file_fetcher = match base_dir {
Some(dir) => FileFetcher::with_base_dir(dir),
None => FileFetcher::new(),
};
#[cfg(feature = "ureq")]
let inner = CombinedFetcher::new()
.with_fetcher(file_fetcher)
.with_fetcher(UreqFetcher::new());
#[cfg(not(feature = "ureq"))]
let inner = CombinedFetcher::new().with_fetcher(file_fetcher);
Self {
inner,
cache: DashMap::new(),
}
}
#[cfg(feature = "ureq")]
pub fn timeout(self, secs: u64) -> Self {
let file_fetcher = FileFetcher::new();
let inner = CombinedFetcher::new()
.with_fetcher(file_fetcher)
.with_fetcher(UreqFetcher::new().timeout(secs));
Self {
inner,
cache: self.cache,
}
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
}
impl Default for DefaultFetcher {
fn default() -> Self {
Self::new()
}
}
impl SchemaFetcher for DefaultFetcher {
fn fetch(&self, url: &str) -> crate::error::Result<FetchResult> {
if let Some(entry) = self.cache.get(url) {
return Ok(entry.value().clone());
}
let result = self.inner.fetch(url)?;
self.cache.insert(url.to_string(), result.clone());
if result.final_url != url {
self.cache.insert(result.final_url.clone(), result.clone());
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_noop_fetcher() {
let fetcher = NoopFetcher;
let result = fetcher.fetch("http://example.com/schema.xsd");
assert!(result.is_err());
}
#[test]
fn test_noop_fetcher_error_message() {
let fetcher = NoopFetcher;
let result = fetcher.fetch("http://example.com/test.xsd");
let err = result.unwrap_err();
let msg = format!("{}", err);
assert!(msg.contains("network"));
assert!(msg.contains("http://example.com/test.xsd"));
}
#[test]
fn test_fetch_result_struct() {
let result = FetchResult {
content: vec![1, 2, 3],
final_url: "http://example.com/final.xsd".to_string(),
redirected: true,
};
assert_eq!(result.content, vec![1, 2, 3]);
assert_eq!(result.final_url, "http://example.com/final.xsd");
assert!(result.redirected);
}
#[test]
fn test_fetch_result_no_redirect() {
let result = FetchResult {
content: b"<schema/>".to_vec(),
final_url: "http://example.com/schema.xsd".to_string(),
redirected: false,
};
assert_eq!(result.content, b"<schema/>");
assert!(!result.redirected);
}
#[test]
fn test_fetch_result_clone() {
let result = FetchResult {
content: vec![42],
final_url: "http://example.com".to_string(),
redirected: false,
};
let cloned = result.clone();
assert_eq!(cloned.content, result.content);
assert_eq!(cloned.final_url, result.final_url);
assert_eq!(cloned.redirected, result.redirected);
}
#[test]
fn test_fetch_result_debug() {
let result = FetchResult {
content: vec![],
final_url: "http://test.com".to_string(),
redirected: true,
};
let debug = format!("{:?}", result);
assert!(debug.contains("FetchResult"));
assert!(debug.contains("http://test.com"));
assert!(debug.contains("true"));
}
#[test]
fn test_default_fetcher_new() {
let _fetcher = DefaultFetcher::new();
}
#[test]
fn test_default_fetcher_default() {
let _fetcher = DefaultFetcher::default();
}
#[test]
fn test_default_fetcher_with_base_dir() {
let _fetcher = DefaultFetcher::with_base_dir("/tmp");
}
#[cfg(feature = "ureq")]
#[test]
fn test_default_fetcher_timeout() {
let _fetcher = DefaultFetcher::new().timeout(60);
}
}