use egui::TextureHandle; use hashbrown::{hash_map::RawEntryMut, HashMap}; use notedeck::JobPool; use poll_promise::Promise; #[derive(Default)] pub struct JobsCache { jobs: HashMap, } pub enum JobState { Pending(Promise>>), Error(JobError), Completed(Job), } pub enum JobError { InvalidParameters, } #[derive(Debug)] pub enum JobParams<'a> { Blurhash(BlurhashParams<'a>), } #[derive(Debug)] pub enum JobParamsOwned { Blurhash(BlurhashParamsOwned), } impl<'a> From> for BlurhashParamsOwned { fn from(params: BlurhashParams<'a>) -> Self { BlurhashParamsOwned { blurhash: params.blurhash.to_owned(), url: params.url.to_owned(), ctx: params.ctx.clone(), } } } impl<'a> From> for JobParamsOwned { fn from(params: JobParams<'a>) -> Self { match params { JobParams::Blurhash(bp) => JobParamsOwned::Blurhash(bp.into()), } } } #[derive(Debug)] pub struct BlurhashParams<'a> { pub blurhash: &'a str, pub url: &'a str, pub ctx: &'a egui::Context, } #[derive(Debug)] pub struct BlurhashParamsOwned { pub blurhash: String, pub url: String, pub ctx: egui::Context, } impl JobsCache { pub fn get_or_insert_with< 'a, F: FnOnce(Option) -> Result + Send + 'static, >( &'a mut self, job_pool: &mut JobPool, jobid: &JobId, params: Option, run_job: F, ) -> &'a mut JobState { match self.jobs.raw_entry_mut().from_key(jobid) { RawEntryMut::Occupied(entry) => 's: { let mut state = entry.into_mut(); let JobState::Pending(promise) = &mut state else { break 's state; }; let Some(res) = promise.ready_mut() else { break 's state; }; let Some(res) = res.take() else { tracing::error!("Failed to take the promise for job: {:?}", jobid); break 's state; }; *state = match res { Ok(j) => JobState::Completed(j), Err(e) => JobState::Error(e), }; state } RawEntryMut::Vacant(entry) => { let owned_params = params.map(JobParams::into); let wrapped: Box Option> + Send + 'static> = Box::new(move || Some(run_job(owned_params))); let promise = Promise::spawn_async(job_pool.schedule(wrapped)); let (_, state) = entry.insert(jobid.into(), JobState::Pending(promise)); state } } } pub fn get(&self, jobid: &JobId) -> Option<&JobState> { self.jobs.get(jobid) } } impl<'a> From<&JobId<'a>> for JobIdOwned { fn from(jobid: &JobId<'a>) -> Self { match jobid { JobId::Blurhash(s) => JobIdOwned::Blurhash(s.to_string()), } } } impl hashbrown::Equivalent for JobId<'_> { fn equivalent(&self, key: &JobIdOwned) -> bool { match (self, key) { (JobId::Blurhash(a), JobIdOwned::Blurhash(b)) => *a == b.as_str(), } } } #[derive(Debug, PartialEq, Eq, Clone, Hash)] enum JobIdOwned { Blurhash(String), // image URL } #[derive(Debug, Hash)] pub enum JobId<'a> { Blurhash(&'a str), // image URL } pub enum Job { Blurhash(Option), } impl std::fmt::Debug for Job { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { Job::Blurhash(_) => write!(f, "Blurhash"), } } }