gabrielsimmer.com/src/cache/mod.rs

136 lines
3.5 KiB
Rust
Raw Normal View History

mod memory;
mod sqlite;
use async_trait::async_trait;
use sqlx::FromRow;
2023-10-02 15:33:19 +01:00
use std::{
fmt,
time::{SystemTime, UNIX_EPOCH},
};
use self::{memory::Memory, sqlite::Sqlite};
#[derive(Clone, Debug)]
pub struct Cache {
memory: Memory,
sqlite: Option<Sqlite>,
}
pub async fn init_cache() -> Cache {
2023-10-02 15:33:19 +01:00
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,
2023-10-02 15:33:19 +01:00
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)]
2023-10-02 15:33:19 +01:00
tier: Option<Tier>,
}
impl CachedItem {
pub fn tier(&self) -> Tier {
self.tier.clone().unwrap_or(Tier::None)
}
}
#[async_trait]
2023-10-02 15:33:19 +01:00
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();
2023-10-02 15:33:19 +01:00
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;
2023-10-02 15:33:19 +01:00
return s;
}
}
2023-10-02 15:33:19 +01:00
return None;
}
2023-10-02 15:33:19 +01:00
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();
2023-10-02 15:33:19 +01:00
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 {
2023-10-02 15:33:19 +01:00
Tier::Memory => 2 * 60,
Tier::Sqlite => 10 * 60,
Tier::External => 0,
Tier::None => 0,
};
2023-10-02 15:33:19 +01:00
let current_time: i64 = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("SystemTime before UNIX EPOCH!")
2023-10-02 15:33:19 +01:00
.as_secs()
.try_into()
.unwrap();
current_time <= (item.cached + cache_time) && item.content != ""
}