use crate::report::{CompactScanReport, FileVersion, ScanReport};
use crate::watch::WatchPlan;
use std::path::{Path, PathBuf};
pub const SCAN_CACHE_FORMAT_VERSION: u32 = 2;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScanCacheEntry {
pub relative: String,
pub bytes: u64,
pub content_hash: String,
#[cfg_attr(feature = "serde", serde(default))]
pub content_fingerprint: String,
pub version: FileVersion,
pub binary_checked: bool,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScanCache {
pub format_version: u32,
#[cfg_attr(feature = "serde", serde(with = "crate::path_serde"))]
pub root: PathBuf,
pub entries: Vec<ScanCacheEntry>,
}
impl ScanCache {
#[must_use]
pub fn from_report(report: &ScanReport) -> Self {
let entries = report
.files
.iter()
.filter_map(|file| {
let content_hash = file
.content_hash
.as_ref()
.filter(|hash| hash.starts_with("sha256:"))?
.clone();
Some(ScanCacheEntry {
relative: file.relative.clone(),
bytes: file.bytes,
content_hash,
content_fingerprint: file.content_fingerprint.clone()?,
version: file.version,
binary_checked: file.binary_checked,
})
})
.collect();
Self {
format_version: SCAN_CACHE_FORMAT_VERSION,
root: report.root.clone(),
entries,
}
}
#[must_use]
pub fn is_compatible(&self, root: &Path) -> bool {
self.format_version == SCAN_CACHE_FORMAT_VERSION && self.root == root
}
pub fn invalidate<'a, I>(&mut self, relative_paths: I) -> usize
where
I: IntoIterator<Item = &'a str>,
{
let paths = relative_paths
.into_iter()
.collect::<std::collections::HashSet<_>>();
let before = self.entries.len();
self.entries
.retain(|entry| !paths.contains(entry.relative.as_str()));
before - self.entries.len()
}
pub fn apply_watch_plan(&mut self, plan: &WatchPlan) -> usize {
if plan.full_rescan {
let removed = self.entries.len();
self.entries.clear();
return removed;
}
self.invalidate(plan.invalidated())
}
}
impl ScanReport {
#[must_use]
pub fn to_cache(&self) -> ScanCache {
ScanCache::from_report(self)
}
}
impl CompactScanReport {
#[must_use]
pub fn to_cache(&self) -> ScanCache {
let entries = self
.files
.iter()
.filter_map(|file| {
let content = file.content.as_deref()?;
let content_hash = content
.content_hash
.as_deref()
.filter(|hash| hash.starts_with("sha256:"))?
.to_owned();
Some(ScanCacheEntry {
relative: file.relative.to_string(),
bytes: file.bytes,
content_hash,
content_fingerprint: content.content_fingerprint.as_deref()?.to_owned(),
version: content.version,
binary_checked: content.binary_checked,
})
})
.collect();
ScanCache {
format_version: SCAN_CACHE_FORMAT_VERSION,
root: self.root.clone(),
entries,
}
}
}