fxrs_core/
read_evidence.rs1use std::collections::HashMap;
2use std::path::{Path, PathBuf};
3use std::sync::RwLock;
4
5pub type ContentHash = [u8; 32];
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub struct ReadEvidence {
11 pub modified_ns: i128,
12 pub content_hash: ContentHash,
13 pub model_view_covers_full_file: bool,
14 pub snapshot_covers_full_file: bool,
15}
16
17pub trait ReadEvidenceStore: Send + Sync {
22 fn record(&self, path: PathBuf, evidence: ReadEvidence);
23 fn lookup(&self, path: &Path) -> Option<ReadEvidence>;
24 fn remove(&self, path: &Path);
25}
26
27#[derive(Debug, Default)]
29pub struct MemoryReadEvidenceStore {
30 entries: RwLock<HashMap<PathBuf, ReadEvidence>>,
31}
32
33impl ReadEvidenceStore for MemoryReadEvidenceStore {
34 fn record(&self, path: PathBuf, evidence: ReadEvidence) {
35 self.entries
36 .write()
37 .unwrap_or_else(std::sync::PoisonError::into_inner)
38 .insert(path, evidence);
39 }
40
41 fn lookup(&self, path: &Path) -> Option<ReadEvidence> {
42 self.entries
43 .read()
44 .unwrap_or_else(std::sync::PoisonError::into_inner)
45 .get(path)
46 .copied()
47 }
48
49 fn remove(&self, path: &Path) {
50 self.entries
51 .write()
52 .unwrap_or_else(std::sync::PoisonError::into_inner)
53 .remove(path);
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn records_overwrites_and_removes_evidence() {
63 let store = MemoryReadEvidenceStore::default();
64 let path = PathBuf::from("/tmp/file.txt");
65 let first = ReadEvidence {
66 modified_ns: 1,
67 content_hash: [1; 32],
68 model_view_covers_full_file: true,
69 snapshot_covers_full_file: true,
70 };
71 let second = ReadEvidence {
72 modified_ns: 2,
73 content_hash: [2; 32],
74 model_view_covers_full_file: false,
75 snapshot_covers_full_file: true,
76 };
77
78 store.record(path.clone(), first);
79 assert_eq!(store.lookup(&path), Some(first));
80 store.record(path.clone(), second);
81 assert_eq!(store.lookup(&path), Some(second));
82 store.remove(&path);
83 assert_eq!(store.lookup(&path), None);
84 }
85}