use std::cell::RefCell;
use std::collections::{BTreeMap, HashMap};
use std::rc::Rc;
use super::client::PlmClient;
pub type Fetched = Option<(String, Vec<u8>)>;
enum Entry {
Pending,
Done(Fetched),
}
thread_local! {
static CACHE: RefCell<HashMap<String, Entry>> = RefCell::new(HashMap::new());
}
fn safe_id(id: &str) -> bool {
!id.is_empty() && id.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-')
}
pub fn request(client: &Rc<PlmClient>, ids: &[String]) {
for id in ids {
if !safe_id(id) {
continue;
}
let fresh = CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
if cache.contains_key(id) {
return false;
}
cache.insert(id.clone(), Entry::Pending);
true
});
if !fresh {
continue;
}
let (client, id) = (client.clone(), id.clone());
crate::store::spawn_waking(async move {
let got = fetch(&client, &id).await;
CACHE.with(|cache| cache.borrow_mut().insert(id, Entry::Done(got)));
});
}
}
async fn fetch(client: &PlmClient, id: &str) -> Fetched {
let response = client.call("GET", &format!("/api/tools/{id}/picture"), None).await.ok()?;
if response.status != 200 || response.body.is_empty() {
return None;
}
let media = response
.headers
.iter()
.find(|(name, _)| name.eq_ignore_ascii_case("content-type"))
.map(|(_, value)| value.clone())
.unwrap_or_default();
Some((media, response.body))
}
pub fn pending_of(ids: &[String]) -> usize {
CACHE.with(|cache| {
let cache = cache.borrow();
ids.iter().filter(|id| matches!(cache.get(id.as_str()), Some(Entry::Pending))).count()
})
}
pub fn pending() -> usize {
CACHE.with(|cache| cache.borrow().values().filter(|entry| matches!(entry, Entry::Pending)).count())
}
pub fn fetched(ids: &[String]) -> BTreeMap<String, Fetched> {
CACHE.with(|cache| {
let cache = cache.borrow();
ids.iter()
.filter_map(|id| match cache.get(id.as_str()) {
Some(Entry::Done(got)) => Some((id.clone(), got.clone())),
_ => None,
})
.collect()
})
}
pub fn png_of(id: &str) -> Option<Vec<u8>> {
CACHE.with(|cache| match cache.borrow().get(id) {
Some(Entry::Done(Some((media, bytes)))) if media.split(';').next().unwrap_or("").trim().eq_ignore_ascii_case("image/png") => Some(bytes.clone()),
_ => None,
})
}
pub fn clear() {
CACHE.with(|cache| cache.borrow_mut().clear());
}