use std::time::Duration;
use thiserror::Error;
use serde::{Serialize, Deserialize};
use diesel::prelude::*;
use rand::TryRngCore;
#[expect(unused_imports, reason = "Used in docs.")]
use crate::types::*;
use crate::util::*;
pub mod path;
pub use path::*;
pub mod inner;
pub use inner::*;
pub mod outer;
pub use outer::*;
pub mod glue;
pub use glue::*;
#[derive(Debug, Clone, Copy)]
pub struct CacheHandle<'a> {
pub cache: &'a Cache,
pub config: CacheHandleConfig
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct CacheHandleConfig {
#[serde(default, skip_serializing_if = "is_default")]
pub delay: bool,
#[serde(default = "get_true", skip_serializing_if = "is_true")]
pub read: bool,
#[serde(default = "get_true", skip_serializing_if = "is_true")]
pub write: bool
}
impl Default for CacheHandleConfig {
fn default() -> Self {
Self {
delay: false,
read : true,
write: true
}
}
}
impl CacheHandle<'_> {
pub fn read(&self, keys: CacheEntryKeys) -> Result<Option<CacheEntryValues>, ReadFromCacheError> {
if self.config.read {
let ret = self.cache.read(keys)?;
if self.config.delay && let Some(CacheEntryValues {duration, ..}) = ret {
let between_neg_1_and_1 = rand::rngs::OsRng.try_next_u32().expect("Os RNG to be available") as f32 / f32::MAX * 2.0 - 1.0;
std::thread::sleep(duration.mul_f32(1.0 + between_neg_1_and_1 / 8.0));
}
Ok(ret)
} else {
Ok(None)
}
}
pub fn write(&self, entry: NewCacheEntry) -> Result<(), WriteToCacheError> {
if self.config.write {
self.cache.write(entry)
} else {
Ok(())
}
}
}
diesel::table! {
cache (subject, key) {
subject -> Text,
key -> Text,
value -> Nullable<Text>,
duration -> Float
}
}
pub const DB_INIT_COMMAND: &str = r#"CREATE TABLE cache (
subject TEXT NOT NULL,
"key" TEXT NOT NULL,
value TEXT,
duration FLOAT NOT NULL,
UNIQUE(subject, "key") ON CONFLICT REPLACE
)"#;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Insertable)]
#[diesel(table_name = cache)]
pub struct NewCacheEntry<'a> {
pub subject: &'a str,
pub key: &'a str,
pub value: Option<&'a str>,
#[diesel(serialize_as = DurationGlue)]
pub duration: Duration
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Queryable, Selectable)]
#[diesel(table_name = cache)]
pub struct CacheEntryKeys<'a> {
pub subject: &'a str,
pub key: &'a str,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Queryable, Selectable)]
#[diesel(table_name = cache)]
pub struct CacheEntryValues {
pub value: Option<String>,
#[diesel(deserialize_as = DurationGlue)]
pub duration: Duration
}
#[derive(Debug, Error)]
pub enum ReadFromCacheError {
#[error(transparent)]
DieselError(#[from] diesel::result::Error),
#[error(transparent)]
ConnectCacheError(#[from] ConnectCacheError)
}
#[derive(Debug, Error)]
pub enum WriteToCacheError {
#[error(transparent)]
DieselError(#[from] diesel::result::Error),
#[error(transparent)]
ConnectCacheError(#[from] ConnectCacheError)
}
#[derive(Debug, Error)]
pub enum ConnectCacheError {
#[error(transparent)]
ConnectionError(#[from] diesel::ConnectionError),
#[error(transparent)]
IoError(#[from] std::io::Error),
#[error(transparent)]
DieselError(#[from] diesel::result::Error)
}