use std::future::Future;
use std::time::Duration;
use async_trait::async_trait;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Phase {
Fetch,
Transform,
Render,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CacheTier {
Memory,
Persistent,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MissReason {
NotFound,
Expired,
Invalidated,
}
#[derive(Debug, Clone)]
pub struct ProgressEvent {
pub phase: Phase,
pub source_name: Option<String>,
pub loaded: Option<u64>,
pub total: Option<u64>,
pub message: String,
}
#[derive(Debug, Clone)]
pub struct CacheHitEvent {
pub key: u64,
pub source_name: Option<String>,
pub tier: CacheTier,
pub age: Duration,
}
#[derive(Debug, Clone)]
pub struct CacheMissEvent {
pub key: u64,
pub source_name: Option<String>,
pub reason: MissReason,
}
#[derive(Debug, Clone)]
pub struct ErrorEvent {
pub phase: Phase,
pub source_name: Option<String>,
pub error: String,
}
#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
pub trait ResolverHooks: Send + Sync {
async fn on_progress(&self, _event: ProgressEvent) {}
async fn on_cache_hit(&self, _event: CacheHitEvent) {}
async fn on_cache_miss(&self, _event: CacheMissEvent) {}
async fn on_error(&self, _event: ErrorEvent) {}
}
#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
pub trait ResolverHooks {
async fn on_progress(&self, _event: ProgressEvent) {}
async fn on_cache_hit(&self, _event: CacheHitEvent) {}
async fn on_cache_miss(&self, _event: CacheMissEvent) {}
async fn on_error(&self, _event: ErrorEvent) {}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct NullHooks;
#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl ResolverHooks for NullHooks {}
#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
impl ResolverHooks for NullHooks {}
#[cfg(not(target_arch = "wasm32"))]
pub type HooksRef = std::sync::Arc<dyn ResolverHooks>;
#[cfg(target_arch = "wasm32")]
pub type HooksRef = std::rc::Rc<dyn ResolverHooks>;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) fn spawn_hook<F>(fut: F)
where
F: Future<Output = ()> + Send + 'static,
{
match tokio::runtime::Handle::try_current() {
Ok(handle) => {
let _handle = handle.spawn(fut);
}
Err(_) => {
tracing::warn!(
target: "chartml::resolver::hooks",
"no tokio runtime available; dropping hook event (consider running inside a tokio runtime to receive resolver hooks)"
);
drop(fut);
}
}
}
#[cfg(target_arch = "wasm32")]
pub(crate) fn spawn_hook<F>(fut: F)
where
F: Future<Output = ()> + 'static,
{
wasm_bindgen_futures::spawn_local(fut);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn null_hooks_is_zst() {
assert_eq!(std::mem::size_of::<NullHooks>(), 0);
}
#[test]
fn module_docs_document_panic_free_requirement() {
let module_doc = include_str!("hooks.rs");
assert!(
module_doc.contains("Hooks must be panic-free")
|| module_doc.contains("must not panic"),
"module docs must document the panic-free requirement"
);
assert!(
module_doc.contains("fire-and-forget on the runtime")
|| module_doc.contains("no ordering guarantee"),
"module docs must document the fire-and-forget / no-ordering semantics"
);
}
}