use std::path::{Path, PathBuf};
use dashmap::DashMap;
use crate::error::Result;
use super::traits::AsyncSchemaFetcher;
use super::{AsyncFileFetcher, FetchResult, ReqwestFetcher};
pub struct AsyncDefaultFetcher {
file_fetcher: AsyncFileFetcher,
http_fetcher: ReqwestFetcher,
cache: DashMap<String, FetchResult>,
}
impl AsyncDefaultFetcher {
pub fn new() -> Result<Self> {
Self::with_base_dir_option(None)
}
pub fn with_base_dir(base_dir: impl AsRef<Path>) -> Result<Self> {
Self::with_base_dir_option(Some(base_dir.as_ref().to_path_buf()))
}
fn with_base_dir_option(base_dir: Option<PathBuf>) -> Result<Self> {
let file_fetcher = match base_dir {
Some(dir) => AsyncFileFetcher::with_base_dir(dir),
None => AsyncFileFetcher::new(),
};
let http_fetcher = ReqwestFetcher::new()?;
Ok(Self {
file_fetcher,
http_fetcher,
cache: DashMap::new(),
})
}
pub fn len(&self) -> usize {
self.cache.len()
}
pub fn is_empty(&self) -> bool {
self.cache.is_empty()
}
pub async fn fetch(&self, url: &str) -> Result<FetchResult> {
if let Some(entry) = self.cache.get(url) {
return Ok(entry.value().clone());
}
let result = if let Ok(result) = self.file_fetcher.fetch(url).await {
result
} else {
self.http_fetcher.fetch_async(url).await?
};
self.cache.insert(url.to_string(), result.clone());
if result.final_url != url {
self.cache.insert(result.final_url.clone(), result.clone());
}
Ok(result)
}
}
#[async_trait::async_trait]
impl AsyncSchemaFetcher for AsyncDefaultFetcher {
async fn fetch(&self, url: &str) -> Result<FetchResult> {
AsyncDefaultFetcher::fetch(self, url).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_async_default_fetcher_new() {
let fetcher = AsyncDefaultFetcher::new();
assert!(fetcher.is_ok());
}
#[test]
fn test_async_default_fetcher_with_base_dir() {
let fetcher = AsyncDefaultFetcher::with_base_dir("/tmp");
assert!(fetcher.is_ok());
}
#[tokio::test]
async fn test_async_default_fetcher_local_file() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp_file = NamedTempFile::new().unwrap();
writeln!(temp_file, "test schema content").unwrap();
let path = temp_file.path().to_str().unwrap();
let fetcher = AsyncDefaultFetcher::new().unwrap();
let result = fetcher.fetch(path).await.unwrap();
assert!(result.content.starts_with(b"test schema content"));
assert!(result.final_url.starts_with("file://"));
}
}