aboutsummaryrefslogtreecommitdiff
path: root/cache/src/key.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cache/src/key.rs')
-rw-r--r--cache/src/key.rs83
1 files changed, 83 insertions, 0 deletions
diff --git a/cache/src/key.rs b/cache/src/key.rs
new file mode 100644
index 0000000..d8ca510
--- /dev/null
+++ b/cache/src/key.rs
@@ -0,0 +1,83 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2025 metamuffin <metamuffin.org>
+*/
+use crate::{CACHE_GENERATION_BUCKET_COUNT, CONF};
+use anyhow::bail;
+use base64::{Engine, prelude::BASE64_URL_SAFE};
+use sha2::Sha256;
+use std::{
+ fmt::Display,
+ hash::{Hash, Hasher},
+ str::FromStr,
+};
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct CacheKey(pub [u8; 32]);
+
+impl CacheKey {
+ pub fn new(ty: CacheContentType, seed: impl Hash) -> Self {
+ use sha2::Digest;
+ struct ShaHasher(Sha256);
+ impl Hasher for ShaHasher {
+ fn finish(&self) -> u64 {
+ unreachable!()
+ }
+ fn write(&mut self, bytes: &[u8]) {
+ self.0.update(bytes);
+ }
+ }
+ let mut d = ShaHasher(sha2::Sha256::new());
+ d.0.update(CONF.secret.as_bytes());
+ seed.hash(&mut d);
+ let d = d.0.finalize();
+ let mut key: [u8; 32] = d.as_slice().try_into().unwrap();
+ key[0] = ty as u8;
+ Self(key)
+ }
+ pub fn new_json(seed: impl Hash) -> Self {
+ Self::new(CacheContentType::Json, seed)
+ }
+ pub fn new_image(seed: impl Hash) -> Self {
+ Self::new(CacheContentType::Image, seed)
+ }
+ pub fn content_type(&self) -> CacheContentType {
+ match self.0[0] {
+ 1 => CacheContentType::Image,
+ 2 => CacheContentType::Json,
+ _ => CacheContentType::Unknown,
+ }
+ }
+ pub(super) fn bucket(&self) -> usize {
+ (self.0[1] as usize
+ | ((self.0[2] as usize) << 8)
+ | ((self.0[3] as usize) << 16)
+ | ((self.0[4] as usize) << 24))
+ % CACHE_GENERATION_BUCKET_COUNT
+ }
+}
+
+impl Display for CacheKey {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&BASE64_URL_SAFE.encode(self.0))
+ }
+}
+impl FromStr for CacheKey {
+ type Err = anyhow::Error;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ let mut out = [0; 32];
+ let size = BASE64_URL_SAFE.decode_slice(s, &mut out)?;
+ if size != out.len() {
+ bail!("cache key parse invalid size")
+ }
+ Ok(Self(out))
+ }
+}
+
+#[repr(u8)]
+pub enum CacheContentType {
+ Unknown = 0,
+ Image = 1,
+ Json = 2,
+}