#![allow(non_snake_case)]
mod config;
mod freshness;
#[cfg(not(target_arch = "wasm32"))]
mod fs_cache;
mod memory_cache;
use std::time::Duration;
use chrono::Utc;
pub use config::*;
pub use freshness::*;
use self::memory_cache::InMemoryCache;
pub struct CachedRender<'a> {
pub route: String,
pub freshness: RenderFreshness,
pub response: &'a [u8],
}
pub struct IncrementalRenderer {
pub(crate) memory_cache: InMemoryCache,
#[cfg(not(target_arch = "wasm32"))]
pub(crate) file_system_cache: fs_cache::FileSystemCache,
invalidate_after: Option<Duration>,
}
impl IncrementalRenderer {
pub fn builder() -> IncrementalRendererConfig {
IncrementalRendererConfig::new()
}
pub fn invalidate(&mut self, route: &str) {
self.memory_cache.invalidate(route);
#[cfg(not(target_arch = "wasm32"))]
self.file_system_cache.invalidate(route);
}
pub fn invalidate_all(&mut self) {
self.memory_cache.clear();
#[cfg(not(target_arch = "wasm32"))]
self.file_system_cache.clear();
}
pub fn cache(
&mut self,
route: String,
html: impl Into<Vec<u8>>,
) -> Result<RenderFreshness, IncrementalRendererError> {
let timestamp = Utc::now();
let html = html.into();
#[cfg(not(target_arch = "wasm32"))]
self.file_system_cache
.put(route.clone(), timestamp, html.clone())?;
self.memory_cache.put(route, timestamp, html);
Ok(RenderFreshness::created_at(
timestamp,
self.invalidate_after,
))
}
pub fn get<'a>(
&'a mut self,
route: &str,
) -> Result<Option<CachedRender<'a>>, IncrementalRendererError> {
let Self {
memory_cache,
#[cfg(not(target_arch = "wasm32"))]
file_system_cache,
..
} = self;
#[allow(unused)]
enum FsGetError {
NotPresent,
Error(IncrementalRendererError),
}
let or_insert = || {
#[cfg(not(target_arch = "wasm32"))]
return match file_system_cache.get(route) {
Ok(Some((freshness, bytes))) => Ok((freshness.timestamp(), bytes)),
Ok(None) => Err(FsGetError::NotPresent),
Err(e) => Err(FsGetError::Error(e)),
};
#[allow(unreachable_code)]
Err(FsGetError::NotPresent)
};
match memory_cache.try_get_or_insert(route, or_insert) {
Ok(Some((freshness, bytes))) => Ok(Some(CachedRender {
route: route.to_string(),
freshness,
response: bytes,
})),
Err(FsGetError::NotPresent) | Ok(None) => Ok(None),
Err(FsGetError::Error(e)) => Err(e),
}
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum IncrementalRendererError {
#[error("RenderError: {0}")]
RenderError(#[from] std::fmt::Error),
#[error("IoError: {0}")]
IoError(#[from] std::io::Error),
#[error("Unknown error: {0}")]
Other(#[from] dioxus_core::CapturedError),
}