use object_store::{local::LocalFileSystem, path::Path};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct CacheIdentity(u128);
impl CacheIdentity {
#[must_use]
pub fn new(namespace: &str) -> Self {
Self::from_bytes(namespace.as_bytes())
}
fn from_bytes(namespace: &[u8]) -> Self {
let version = Uuid::new_v5(&Uuid::NAMESPACE_URL, b"moraine/cache-identity/v2");
Self(Uuid::new_v5(&version, namespace).as_u128())
}
pub fn local(store: &LocalFileSystem) -> object_store::Result<Self> {
let member = store.path_to_filesystem(&Path::from(".moraine-cache-identity"))?;
let mut namespace = b"local:".to_vec();
namespace.extend_from_slice(member.as_os_str().as_encoded_bytes());
Ok(Self::from_bytes(&namespace))
}
pub(crate) fn directory(self) -> String {
Uuid::from_u128(self.0).simple().to_string()
}
}
impl Default for CacheIdentity {
fn default() -> Self {
Self(Uuid::new_v4().as_u128())
}
}
#[cfg(test)]
mod tests {
use foyer::Code;
use proptest::prelude::*;
use super::*;
proptest! {
#[test]
fn cache_identity_roundtrips(value in any::<u128>()) {
let identity = CacheIdentity(value);
let mut bytes = Vec::new();
identity.encode(&mut bytes).unwrap();
prop_assert_eq!(CacheIdentity::decode(&mut bytes.as_slice()).unwrap(), identity);
}
}
#[test]
fn stable_names_are_versioned_and_isolated_names_are_unique() {
let name = "AmazonS3(bucket)";
assert_eq!(CacheIdentity::new(name), CacheIdentity::new(name));
assert_ne!(
CacheIdentity::new(name).0,
Uuid::new_v5(&Uuid::NAMESPACE_URL, name.as_bytes()).as_u128()
);
assert_ne!(CacheIdentity::default(), CacheIdentity::default());
}
}