use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::time::SystemTime;
use crate::scanner::{FileEntry, Hash};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct CacheEntry {
pub path: PathBuf,
pub size: u64,
pub mtime: SystemTime,
pub inode: Option<u64>,
pub prehash: Hash,
pub fullhash: Option<Hash>,
}
impl CacheEntry {
#[must_use]
pub fn generate_key(path: &Path, size: u64, mtime: SystemTime, inode: Option<u64>) -> String {
format!(
"{}:{}:{}:{}",
path.to_string_lossy(),
size,
mtime
.duration_since(SystemTime::UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0),
inode.unwrap_or(0)
)
}
#[must_use]
pub fn is_valid(&self, size: u64, mtime: SystemTime, inode: Option<u64>) -> bool {
if self.size != size || self.mtime != mtime {
return false;
}
match (self.inode, inode) {
(Some(a), Some(b)) => a == b,
_ => true,
}
}
}
impl From<FileEntry> for CacheEntry {
fn from(entry: FileEntry) -> Self {
Self {
path: entry.path,
size: entry.size,
mtime: entry.modified,
inode: None, prehash: [0u8; 32],
fullhash: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_cache_entry_is_valid() {
let now = SystemTime::now();
let entry = CacheEntry {
path: PathBuf::from("/test.txt"),
size: 100,
mtime: now,
inode: Some(123),
prehash: [0u8; 32],
fullhash: None,
};
assert!(entry.is_valid(100, now, Some(123)));
assert!(entry.is_valid(100, now, None)); assert!(!entry.is_valid(101, now, Some(123)));
assert!(!entry.is_valid(100, now + Duration::from_secs(1), Some(123)));
assert!(!entry.is_valid(100, now, Some(456)));
}
#[test]
fn test_generate_key() {
let now = SystemTime::now();
let key1 = CacheEntry::generate_key(Path::new("/test.txt"), 100, now, Some(123));
let key2 = CacheEntry::generate_key(Path::new("/test.txt"), 100, now, Some(123));
let key3 = CacheEntry::generate_key(Path::new("/test.txt"), 101, now, Some(123));
assert_eq!(key1, key2);
assert_ne!(key1, key3);
}
}