use crate::cache::{Cache, FileCache};
use crate::cached_tracker::CachedTracker;
use crate::error::Result;
use crate::tracker::Package;
use crate::tracker::TrackerContext;
use crate::tracker::get_handler;
use crate::url_store::AnnotatedUrl;
use tokio::sync::Mutex;
pub struct Context {
pub cache_seconds: usize,
pub use_cache: bool,
pub filters: Filters,
pub default_postcode: Option<String>,
pub preferred_language: String,
}
impl Default for Context {
fn default() -> Self {
Self {
preferred_language: "en".to_string(),
cache_seconds: 0,
use_cache: true,
filters: Filters::default(),
default_postcode: None,
}
}
}
#[derive(Default)]
pub struct Filters {
pub url: Option<String>,
pub sender: Option<String>,
pub carrier: Option<String>,
pub recipient: Option<String>,
}
pub struct Job {
pub url: AnnotatedUrl,
pub result: Result<Package>,
}
pub async fn track_url(
url: &AnnotatedUrl,
cache: &Mutex<dyn Cache>,
ctx: &Context,
) -> Job {
let tracker = match get_handler(&url.url) {
Ok(tracker) => tracker,
Err(err) => {
return Job {
url: url.clone(),
result: Err(err),
};
}
};
let mut tracker = CachedTracker {
tracker: tracker,
cache: cache,
};
let tracker_context = TrackerContext {
recipient_postcode: ctx.default_postcode.as_deref(),
language: &ctx.preferred_language,
};
let result = tracker
.track(&url.url, ctx.cache_seconds, ctx.use_cache, &tracker_context)
.await;
Job {
url: url.clone(),
result,
}
}
pub async fn track_urls(
urls: Vec<AnnotatedUrl>,
cache: FileCache,
ctx: &Context,
) -> Result<Vec<Job>> {
let cache = Mutex::new(cache);
let tasks: Vec<_> = urls
.iter()
.map(|url| track_url(url, &cache, ctx))
.collect();
let mut jobs = futures::future::join_all(tasks).await;
{
let cache = cache.lock().await;
if cache.modified {
cache.save()?;
}
}
if let Some(query) = &ctx.filters.recipient {
jobs = jobs
.into_iter()
.filter(|job| match &job.result {
Ok(package) => match package.recipient.as_ref() {
Some(recipient) => recipient
.to_lowercase()
.contains(&query.to_lowercase()),
None => false,
},
Err(_) => true, })
.collect();
}
if let Some(query) = &ctx.filters.sender {
jobs = jobs
.into_iter()
.filter(|job| match &job.result {
Ok(package) => match package.sender.as_ref() {
Some(sender) => sender
.to_lowercase()
.contains(&query.to_lowercase()),
None => false,
},
Err(_) => true, })
.collect();
}
if let Some(query) = &ctx.filters.carrier {
jobs = jobs
.into_iter()
.filter(|job| match &job.result {
Ok(package) => package
.channel
.to_lowercase()
.contains(&query.to_lowercase()),
Err(_) => true, })
.collect();
}
Ok(jobs)
}