122 lines
3.4 KiB
Rust
122 lines
3.4 KiB
Rust
|
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 != ""
|
||
|
}
|