First take on index support
This commit is contained in:
parent
4537cc34dd
commit
46656418d9
1 changed files with 92 additions and 17 deletions
109
src/storage.rs
109
src/storage.rs
|
@ -1,22 +1,95 @@
|
||||||
use std::cmp::Ordering;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
pub struct Version(pub u64);
|
pub struct Version(pub u64);
|
||||||
|
|
||||||
struct Index;
|
impl Version {
|
||||||
|
fn next(self) -> Self {
|
||||||
|
Version(self.0 + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Index {
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
struct Hash([u8; 32]);
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct IndexHashEntry {
|
||||||
|
source_paths: HashSet<String>,
|
||||||
|
storage_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct IndexPathEntry {
|
||||||
|
hash: Hash,
|
||||||
|
version: Version,
|
||||||
|
storage_path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Index<'a> {
|
||||||
|
file_hashes: HashMap<Hash, IndexHashEntry>,
|
||||||
|
file_paths: HashMap<&'a str, IndexPathEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Index<'a> {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {}
|
Self {
|
||||||
|
file_hashes: HashMap::new(),
|
||||||
|
file_paths: HashMap::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn store(&mut self, path: &str, hash: &[u8]) -> (Version, String) {
|
fn store(&mut self, source_path: &'a str, hash: Hash) -> (Version, String) {
|
||||||
(Version(0), "".to_string())
|
let path_entry = {
|
||||||
|
self.file_paths.get(source_path).map_or_else(
|
||||||
|
|| IndexPathEntry {
|
||||||
|
hash,
|
||||||
|
version: Version(0),
|
||||||
|
storage_path: format!("{:X?}", hash.0),
|
||||||
|
},
|
||||||
|
|old_entry| {
|
||||||
|
if old_entry.hash == hash {
|
||||||
|
old_entry.clone() // FIXME optimise
|
||||||
|
} else {
|
||||||
|
IndexPathEntry {
|
||||||
|
hash,
|
||||||
|
version: old_entry.version.next(),
|
||||||
|
storage_path: old_entry.storage_path.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.file_paths.insert(source_path, path_entry.clone());
|
||||||
|
|
||||||
|
let hash_entry = {
|
||||||
|
self.file_hashes.get(&hash).map_or_else(
|
||||||
|
|| {
|
||||||
|
let mut source_paths = HashSet::new();
|
||||||
|
source_paths.insert(source_path.to_string());
|
||||||
|
IndexHashEntry {
|
||||||
|
source_paths,
|
||||||
|
storage_path: format!("{:X?}", hash.0),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|old_entry| {
|
||||||
|
let mut source_paths = old_entry.source_paths.clone();
|
||||||
|
source_paths.insert(source_path.to_string());
|
||||||
|
IndexHashEntry {
|
||||||
|
source_paths,
|
||||||
|
storage_path: old_entry.storage_path.clone()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.file_hashes.insert(hash, hash_entry);
|
||||||
|
(path_entry.version, path_entry.storage_path.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn version(&self, hash: &[u8]) -> Version {
|
fn latest_version_for_path(&self, path: &str) -> Option<Version> {
|
||||||
Version(0)
|
self.file_paths.get(path).map_or(None, |entry| Some(entry.version))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,14 +98,16 @@ mod should {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
const SOME_HASH: Hash = Hash([1; 32]);
|
||||||
|
const SOME_OTHER_HASH: Hash = Hash([2; 32]);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn support_file_versions() {
|
fn support_file_versions() {
|
||||||
let mut index = Index::new();
|
let mut index = Index::new();
|
||||||
let (v1, _) = index.store("/some/path", "some hash".as_bytes());
|
let (v1, _) = index.store("/some/path", SOME_HASH);
|
||||||
let (v2, _) = index.store("/some/path", "some other hash".as_bytes());
|
let (v2, _) = index.store("/some/path", SOME_OTHER_HASH);
|
||||||
|
|
||||||
assert_eq!(v1, index.version("some hash".as_bytes()));
|
assert_eq!(v2, index.latest_version_for_path("/some/path").unwrap());
|
||||||
assert_eq!(v2, index.version("some other hash".as_bytes()));
|
|
||||||
|
|
||||||
assert!(v2 > v1);
|
assert!(v2 > v1);
|
||||||
}
|
}
|
||||||
|
@ -40,11 +115,11 @@ mod should {
|
||||||
#[test]
|
#[test]
|
||||||
fn support_deduplication() {
|
fn support_deduplication() {
|
||||||
let mut index = Index::new();
|
let mut index = Index::new();
|
||||||
let (_, storage_path1) = index.store("/some/path", "same hash".as_bytes());
|
let (_, storage_path1) = index.store("/some/path", SOME_HASH);
|
||||||
let (_, storage_path2) = index.store("/some/path", "same hash".as_bytes());
|
let (_, storage_path2) = index.store("/some/path", SOME_HASH);
|
||||||
let (_, storage_path3) = index.store("/some/other/path", "same hash".as_bytes());
|
let (_, storage_path3) = index.store("/some/other/path", SOME_HASH);
|
||||||
|
|
||||||
assert_eq!(storage_path1, storage_path2);
|
assert_eq!(storage_path1, storage_path2);
|
||||||
assert_ne!(storage_path1, storage_path3);
|
assert_eq!(storage_path1, storage_path3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue