gabrielsimmer.com/src/cache/mod.rs

122 lines
3.4 KiB
Rust
Raw Normal View History

mod memory;
mod sqlite;
use std::{time::{SystemTime, UNIX_EPOCH}, fmt};
use async_trait::async_trait;
use sqlx::FromRow;
use self::{memory::Memory, sqlite::Sqlite};
#[derive(Clone, Debug)]
pub struct Cache {
memory: Memory,
sqlite: Option<Sqlite>,
}
pub async fn init_cache() -> Cache {
Cache{ memory: memory::new(), sqlite: sqlite::new().await }
}
/// Tier enums take an i64, which is the amount of time in seconds
/// the tier should consider the contents of the cache valid.
#[derive(Clone, Debug, sqlx::Type)]
pub enum Tier {
Memory,
Sqlite,
External,
None
}
impl fmt::Display for Tier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Tier::Memory => write!(f, "memory"),
Tier::Sqlite => write!(f, "sqlite"),
Tier::External => write!(f, "external"),
Tier::None => write!(f, ""),
}
}
}
#[derive(Clone, Debug, FromRow)]
pub struct CachedItem {
pub content_type: String,
pub content: String,
cached: i64,
#[sqlx(default)]
tier: Option<Tier>
}
impl CachedItem {
pub fn tier(&self) -> Tier {
self.tier.clone().unwrap_or(Tier::None)
}
}
#[async_trait]
pub trait CacheMechanism: Sized + Clone + Send + Sync + 'static {
async fn get(&self, key: &String) -> Option<CachedItem>;
async fn rm(&mut self, key: String);
async fn set(&self, key: String, item: CachedItem);
}
impl Cache {
pub async fn get(&self, key: &String) -> Option<CachedItem> {
let m = self.memory.get(key).await;
if m.is_some() {
return m;
}
if self.sqlite.is_some() {
let sq = self.sqlite.clone().unwrap();
let s = sq.get(key).await; if s.is_some() {
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs()
.try_into()
.unwrap();
let mut refresh_memory = s.clone().unwrap();
refresh_memory.cached = current_time;
let _ = self.memory.set(key.clone(), refresh_memory).await;
return s
}
}
return None
}
pub async fn set(&self, key: String, content_type: String, content: String) -> bool {
let current_time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs()
.try_into()
.unwrap();
let cached_item = CachedItem{ content_type, content, cached: current_time, tier: None };
self.memory.set(key.clone(), cached_item.clone()).await;
if self.sqlite.is_some() {
let sq = self.sqlite.clone().unwrap();
sq.set(key.clone(), cached_item.clone()).await;
}
true
}
}
/// Determine whether we should actually use the cached item or not.
fn should_use(item: &CachedItem, tier: Tier) -> bool {
// TODO: Make configurable.
let cache_time = match tier {
Tier::Memory => 2*60,
Tier::Sqlite => 10*60,
Tier::External => 0,
Tier::None => 0,
};
let current_time: i64 = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
.as_secs().try_into().unwrap();
current_time <= (item.cached + cache_time) && item.content != ""
}