Skip to main content

flix_tmdb/
cache.rs

1use std::path::Path;
2use std::time::{SystemTime, UNIX_EPOCH};
3
4use bytes::Bytes;
5use redb::{Database, DatabaseError, ReadableDatabase, TableDefinition};
6
7/// The client cache policy
8pub enum CachePolicy {
9	/// Do not use a cache
10	None,
11	/// Use and update the cache
12	Full,
13	/// Use the cache but don't update it
14	Read,
15	/// Ignore the cache but update it
16	Update,
17}
18
19/// The trait representing a caching backend
20pub trait Cache {
21	/// Get a cached value, or None
22	fn get(&self, query: &str) -> Option<Bytes>;
23	/// Set a value in the cache
24	fn set(&self, query: &str, response: &Bytes);
25}
26
27const TABLE: TableDefinition<&str, (u64, &[u8])> = TableDefinition::new("tmdb_responses");
28
29/// A [Cache] implementation using [redb] as the backend
30pub struct RedbCache {
31	db: Database,
32}
33
34impl RedbCache {
35	/// Create/open a [redb] database at the path
36	pub fn new(path: &Path) -> Result<Self, DatabaseError> {
37		Ok(Self {
38			db: Database::create(path)?,
39		})
40	}
41
42	/// Helper function allowing for `.ok()?`
43	fn write(&self, timestamp: u64, query: &str, response: &Bytes) -> Option<()> {
44		let write_txn = self.db.begin_write().ok()?;
45		{
46			let mut table = write_txn.open_table(TABLE).ok()?;
47			table
48				.insert(query, (timestamp, response.iter().as_slice()))
49				.ok()?;
50		}
51		write_txn.commit().ok()
52	}
53}
54
55impl Cache for RedbCache {
56	fn get(&self, query: &str) -> Option<Bytes> {
57		let read_txn = self.db.begin_read().ok()?;
58		let table = read_txn.open_table(TABLE).ok()?;
59
60		let result = table.get(query).ok()??;
61		let (timestamp, data) = result.value();
62
63		let now = SystemTime::now()
64			.duration_since(UNIX_EPOCH)
65			.map(|d| d.as_secs())
66			.unwrap_or(0);
67
68		if now.saturating_sub(timestamp) >= 60 * 60 * 24 * 30 * 6 {
69			None
70		} else {
71			Some(Bytes::copy_from_slice(data))
72		}
73	}
74
75	fn set(&self, query: &str, response: &Bytes) {
76		let now = SystemTime::now()
77			.duration_since(UNIX_EPOCH)
78			.map(|d| d.as_secs())
79			.unwrap_or(0);
80
81		self.write(now, query, response);
82	}
83}