Extract a repository concept

This commit is contained in:
Cyryl Płotnicki 2019-01-12 14:39:20 +00:00
parent c6b267771e
commit 2a96cd2396
10 changed files with 280 additions and 246 deletions

112
Cargo.lock generated
View file

@ -1,8 +1,36 @@
[[package]]
name = "autocfg"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "bakare" name = "bakare"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"dir-diff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "dir-diff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -13,6 +41,11 @@ name = "bitflags"
version = "1.0.4" version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.6" version = "0.1.6"
@ -34,6 +67,26 @@ dependencies = [
"walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "failure"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "fuchsia-zircon" name = "fuchsia-zircon"
version = "0.3.3" version = "0.3.3"
@ -58,6 +111,22 @@ name = "libc"
version = "0.2.45" version = "0.2.45"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.3.22" version = "0.3.22"
@ -168,6 +237,11 @@ dependencies = [
"time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "rustc-demangle"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "rustc-serialize" name = "rustc-serialize"
version = "0.3.24" version = "0.3.24"
@ -202,6 +276,27 @@ name = "semver-parser"
version = "0.7.0" version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.0.5" version = "3.0.5"
@ -225,6 +320,11 @@ dependencies = [
"winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.2.7" version = "2.2.7"
@ -263,14 +363,22 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum autocfg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e5f34df7a019573fb8bdc7e24a2bfebe51a2a1d6bfdbaeccedb3c41fc574727"
"checksum backtrace 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "b5b493b66e03090ebc4343eb02f94ff944e0cbc9ac6571491d170ba026741eb5"
"checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4" "checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
"checksum dir-diff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce6e50ca36311e494793f7629014dc78cd963ba85cd05968ae06a63b867f0b" "checksum dir-diff 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1cce6e50ca36311e494793f7629014dc78cd963ba85cd05968ae06a63b867f0b"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" "checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
"checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" "checksum gcc 0.3.55 (registry+https://github.com/rust-lang/crates.io-index)" = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2"
"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74" "checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
"checksum proc-macro2 0.4.24 (registry+https://github.com/rust-lang/crates.io-index)" = "77619697826f31a02ae974457af0b29b723e5619e113e9397b8b82c6bd253f09"
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" "checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1"
"checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd" "checksum rand 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8356f47b32624fef5b3301c1be97e5944ecdd595409cc5da11d05f211db6cfbd"
"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a" "checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
@ -283,13 +391,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70" "checksum redox_syscall 0.1.44 (registry+https://github.com/rust-lang/crates.io-index)" = "a84bcd297b87a545980a2d25a0beb72a1f490c31f0a9fde52fca35bfbb1ceb70"
"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5" "checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
"checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a"
"checksum rustc-demangle 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "adacaae16d02b6ec37fdc7acfcddf365978de76d1983d3ee22afc260e1ca9619"
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" "checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum syn 0.15.24 (registry+https://github.com/rust-lang/crates.io-index)" = "734ecc29cd36e8123850d9bf21dfd62ef8300aaa8f879aabaa899721808be37c"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2" "checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2"
"checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c" "checksum time 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "847da467bf0db05882a9e2375934a8a55cffdc9db0d128af1518200260ba1f6c"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" "checksum walkdir 2.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0" "checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View file

@ -9,6 +9,7 @@ license = "AGPL-3.0"
walkdir = "2.2" walkdir = "2.2"
rust-crypto = "0.2" rust-crypto = "0.2"
tempfile = "3.0" tempfile = "3.0"
failure = "0.1"
[dev-dependencies] [dev-dependencies]
rust-crypto = "0.2" rust-crypto = "0.2"

View file

@ -1,93 +1,40 @@
use crate::storage::Version; use std::collections::HashMap;
use std::error::Error;
use std::fs; use std::fs;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use walkdir::DirEntry; use walkdir::DirEntry;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::error::BakareError;
use crate::repository::Repository;
use crate::RepositoryRelativePath;
use crate::Version;
pub struct Engine<'a> { pub struct Engine<'a> {
source_path: &'a Path, source_path: &'a Path,
repository_path: &'a Path, repository: &'a Repository<'a>
} }
trait Index {}
struct InMemoryIndex {}
impl InMemoryIndex {
fn new() -> Self {
InMemoryIndex {}
}
}
impl Index for InMemoryIndex {}
impl<'a> Engine<'a> { impl<'a> Engine<'a> {
pub fn new(source_path: &'a Path, repository_path: &'a Path) -> Self { pub fn new(source_path: &'a Path, repository: &'a Repository) -> Self {
let index = InMemoryIndex::new();
Engine::new_with_index(source_path, repository_path, index)
}
fn new_with_index(source_path: &'a Path, repository_path: &'a Path, index: impl Index) -> Self {
Engine { Engine {
source_path, source_path,
repository_path, repository,
} }
} }
pub fn backup(&self) -> Result<(), io::Error> { pub fn backup(&self) -> Result<(), BakareError> {
let walker = WalkDir::new(self.source_path); let walker = WalkDir::new(self.source_path);
for maybe_entry in walker { for maybe_entry in walker {
let entry = maybe_entry?; let entry = maybe_entry?;
if entry.path() != self.source_path { if entry.path() != self.source_path {
self.process_entry(&entry)?; self.repository.store_entry(&entry)?;
} }
} }
Ok(()) Ok(())
} }
pub fn file_version(&self, path: &Path) -> Version {
Version::Newest
}
fn process_entry(&self, entry: &DirEntry) -> Result<(), io::Error> {
// TODO: remember entry in index
// TODO: store file data
if entry.file_type().is_dir() {
fs::create_dir(self.repository_path.join(entry.file_name()))?;
}
if entry.file_type().is_file() {
fs::copy(entry.path(), self.repository_path.join(entry.file_name()))?;
}
Ok(())
}
}
#[cfg(test)]
mod should {
use super::*;
use tempfile::tempdir;
use crate::source::Source;
#[test]
fn store_file_where_index_tells_it() -> Result<(), io::Error> {
let index = FakeIndex {};
let source = Source::new()?;
let repository = tempdir()?;
let engine = Engine::new_with_index(source.path(), repository.path(), index);
// backup
// see if repo contains one file at the faked path
assert!(false);
Ok(())
}
struct FakeIndex {}
impl Index for FakeIndex {}
} }

21
src/error.rs Normal file
View file

@ -0,0 +1,21 @@
use std::io;
use failure::Fail;
#[derive(Debug, Fail)]
pub enum BakareError {
#[fail(display = "io error")]
IOError,
}
impl From<io::Error> for BakareError {
fn from(e: io::Error) -> Self {
BakareError::IOError
}
}
impl From<walkdir::Error> for BakareError {
fn from(e: walkdir::Error) -> Self {
BakareError::IOError
}
}

View file

@ -1,6 +1,22 @@
use std::collections::HashMap;
use std::fmt::Display;
use std::fmt::Formatter;
use std::io;
use failure::Fail;
pub mod error;
pub mod backup; pub mod backup;
pub mod restore; pub mod restore;
pub mod source; pub mod source;
mod storage; pub mod repository;
pub struct Version(String);
struct RepositoryRelativePath {}
struct Index<'a> {
versions: HashMap<&'a RepositoryRelativePath, &'a Version>,
}

68
src/repository.rs Normal file
View file

@ -0,0 +1,68 @@
use std::error::Error;
use std::fs;
use std::io;
use std::path::Path;
use walkdir::DirEntry;
use crate::error::BakareError;
use crate::Version;
/// represents a place where backup is stored an can be restored from. E.g. a directory, a cloud service etc
pub struct Repository<'a> {
path: &'a Path
}
pub struct StoredItemId;
pub struct RelativePath;
impl<'a> Repository<'a> {
pub fn new(path: &Path) -> Result<Repository, BakareError> {
Ok(Repository {
path
})
}
pub fn store_entry(&self, entry: &DirEntry) -> Result<(), BakareError> {
// get file id -> contents hash + original path + time of taking notes
// get storage path for File
// store file contents
// remember File
if entry.file_type().is_dir() {
fs::create_dir(self.path.join(entry.file_name()))?;
}
if entry.file_type().is_file() {
fs::copy(entry.path(), self.path.join(entry.file_name()))?;
}
Ok(())
}
pub fn newest_version_for(&self, item: &StoredItemId) -> Result<Version, BakareError> {
unimplemented!()
}
pub fn relative_path(&self, path: &str) -> RelativePath {
unimplemented!()
}
pub fn file_id(&self, path: &RelativePath) -> Result<StoredItemId, BakareError> {
unimplemented!()
}
}
impl<'a> Iterator for Repository<'a> {
type Item = StoredItemId;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
unimplemented!()
}
}
impl<'a> Iterator for &Repository<'a> {
type Item = StoredItemId;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
unimplemented!()
}
}

View file

@ -1,52 +1,47 @@
use crate::storage::Version; use std::error::Error;
use std::fs; use std::fs;
use std::io; use std::io;
use std::path::Path; use std::path::Path;
use walkdir::DirEntry; use walkdir::DirEntry;
use walkdir::WalkDir; use walkdir::WalkDir;
use crate::error::BakareError;
use crate::repository::Repository;
use crate::repository::StoredItemId;
use crate::Version;
pub struct Engine<'a> { pub struct Engine<'a> {
repository_path: &'a Path, repository: &'a Repository<'a>,
target_path: &'a Path, target_path: &'a Path,
} }
pub enum WhatToRestore {
All,
SpecificPath(String),
}
impl<'a> Engine<'a> { impl<'a> Engine<'a> {
pub fn new(repository_path: &'a Path, target_path: &'a Path) -> Self { pub fn new(repository: &'a Repository, target_path: &'a Path) -> Self {
Engine { Engine {
repository_path, repository,
target_path, target_path,
} }
} }
pub fn restore_all(&self) -> Result<(), io::Error> { pub fn restore_all(&self) -> Result<(), BakareError> {
self.restore(WhatToRestore::All) for item in self.repository {
} self.restore(item)?;
fn restore(&self, what: WhatToRestore) -> Result<(), io::Error> {
self.restore_as_of_version(what, Version::Newest)
}
pub fn restore_as_of_version(&self, what: WhatToRestore, version: Version) -> Result<(), io::Error> {
let walker = WalkDir::new(self.repository_path);
for maybe_entry in walker {
match maybe_entry {
Ok(entry) => {
if entry.path() != self.repository_path {
self.process_entry(&entry)?;
}
}
Err(error) => return Err(error.into()),
}
} }
Ok(()) Ok(())
} }
fn process_entry(&self, entry: &DirEntry) -> Result<(), io::Error> { fn restore(&self, item: StoredItemId) -> Result<(), BakareError> {
let version = self.repository.newest_version_for(&item)?;
self.restore_as_of_version(item, version)
}
pub fn restore_as_of_version(&self, what: StoredItemId, version: Version) -> Result<(), BakareError> {
unimplemented!()
}
fn process_entry(&self, entry: &DirEntry) -> Result<(), BakareError> {
if entry.file_type().is_dir() { if entry.file_type().is_dir() {
fs::create_dir(self.target_path.join(entry.file_name()))?; fs::create_dir(self.target_path.join(entry.file_name()))?;
} }

View file

@ -2,6 +2,7 @@ use std::fs::File;
use std::io::Error; use std::io::Error;
use std::io::Write; use std::io::Write;
use std::path::Path; use std::path::Path;
use tempfile::tempdir; use tempfile::tempdir;
use tempfile::TempDir; use tempfile::TempDir;

View file

@ -1,131 +0,0 @@
use std::collections::HashMap;
use std::collections::HashSet;
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)]
pub enum Version {
Newest,
Specific(u64),
}
impl Version {
fn next(self) -> Self {
match self {
Version::Newest => Version::Newest,
Version::Specific(old) => Version::Specific(old + 1),
}
}
}
#[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 {
Self {
file_hashes: HashMap::new(),
file_paths: HashMap::new(),
}
}
fn store(&mut self, source_path: &'a str, hash: Hash) -> (Version, String) {
let path_entry = {
self.file_paths.get(source_path).map_or_else(
|| IndexPathEntry {
hash,
version: Version::Newest,
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 latest_version_for_path(&self, path: &str) -> Option<Version> {
self.file_paths.get(path).map_or(None, |entry| Some(entry.version))
}
}
#[cfg(test)]
mod should {
use super::*;
const SOME_HASH: Hash = Hash([1; 32]);
const SOME_OTHER_HASH: Hash = Hash([2; 32]);
#[test]
fn support_file_versions() {
let mut index = Index::new();
let (v1, _) = index.store("/some/path", SOME_HASH);
let (v2, _) = index.store("/some/path", SOME_OTHER_HASH);
assert_eq!(v2, index.latest_version_for_path("/some/path").unwrap());
assert!(v2 > v1);
}
#[test]
fn support_deduplication() {
let mut index = Index::new();
let (_, storage_path1) = index.store("/some/path", SOME_HASH);
let (_, storage_path2) = index.store("/some/path", SOME_HASH);
let (_, storage_path3) = index.store("/some/other/path", SOME_HASH);
assert_eq!(storage_path1, storage_path2);
assert_eq!(storage_path1, storage_path3);
}
}

View file

@ -1,56 +1,60 @@
use bakare::backup; use bakare::backup;
use bakare::restore; use bakare::restore;
use bakare::restore::WhatToRestore::SpecificPath;
use bakare::source::Source; use bakare::source::Source;
use dir_diff::is_different; use dir_diff::is_different;
use std::fs::File; use std::fs::File;
use std::io::Error;
use std::io::Read; use std::io::Read;
use std::path::Path; use std::path::Path;
use tempfile::tempdir; use tempfile::tempdir;
use bakare::error::BakareError;
use bakare::repository::Repository;
#[test] #[test]
fn restore_backed_up_files() -> Result<(), Error> { fn restore_backed_up_files() -> Result<(), BakareError> {
let source = Source::new()?; let source = Source::new()?;
let repository = tempdir()?; let repository_path = tempdir()?.into_path();
let repository = Repository::new(repository_path.as_path())?;
source.write_text_to_file("first", "some contents")?; source.write_text_to_file("first", "some contents")?;
source.write_text_to_file("second", "some contents")?; source.write_text_to_file("second", "some contents")?;
source.write_text_to_file("third", "some other contents")?; source.write_text_to_file("third", "some other contents")?;
assert_same_after_restore(source.path(), repository.path()) assert_same_after_restore(source.path(), &repository)
} }
#[test] #[test]
fn restore_older_version_of_file() -> Result<(), Error> { fn restore_older_version_of_file() -> Result<(), BakareError> {
let source = Source::new()?; let source = Source::new()?;
let repository = tempdir()?; let repository_path = tempdir()?.into_path();
let backup_engine = backup::Engine::new(source.path(), repository.path()); let repository = Repository::new(repository_path.as_path())?;
let path = "some path"; let backup_engine = backup::Engine::new(source.path(), &repository);
let file_path = "some path";
let new_contents = "totally new contents"; let new_contents = "totally new contents";
let restore_target = tempdir()?; let restore_target = tempdir()?;
let restore_engine = restore::Engine::new(repository.path(), &restore_target.path()); let restore_engine = restore::Engine::new(&repository, &restore_target.path());
let old_contents = "some old contents"; let old_contents = "some old contents";
source.write_text_to_file(path, old_contents)?; source.write_text_to_file(file_path, old_contents)?;
backup_engine.backup()?; backup_engine.backup()?;
let old_version = backup_engine.file_version(path.as_ref()); let repository_path = repository.relative_path(file_path);
let file_id = repository.file_id(&repository_path)?;
let old_version = repository.newest_version_for(&file_id)?;
source.write_text_to_file(path, new_contents)?; source.write_text_to_file(file_path, new_contents)?;
backup_engine.backup()?; backup_engine.backup()?;
restore_engine.restore_as_of_version(SpecificPath(path.into()), old_version)?; restore_engine.restore_as_of_version(file_id, old_version)?;
assert_target_file_contents(restore_target.path(), path, old_contents)?; assert_target_file_contents(restore_target.path(), file_path, old_contents)?;
Ok(()) Ok(())
} }
// TODO: restore latest version by default // TODO: restore latest version by default
// TODO: deduplicate data // TODO: deduplicate data
fn assert_target_file_contents(target: &Path, filename: &str, expected_contents: &str) -> Result<(), Error> { fn assert_target_file_contents(target: &Path, filename: &str, expected_contents: &str) -> Result<(), BakareError> {
let restored_path = target.join(filename); let restored_path = target.join(filename);
let mut actual_contents = String::new(); let mut actual_contents = String::new();
File::open(restored_path)?.read_to_string(&mut actual_contents)?; File::open(restored_path)?.read_to_string(&mut actual_contents)?;
@ -58,12 +62,12 @@ fn assert_target_file_contents(target: &Path, filename: &str, expected_contents:
Ok(()) Ok(())
} }
fn assert_same_after_restore(source_path: &Path, repository_path: &Path) -> Result<(), Error> { fn assert_same_after_restore(source_path: &Path, repository: &Repository) -> Result<(), BakareError> {
let backup_engine = backup::Engine::new(source_path, repository_path); let backup_engine = backup::Engine::new(source_path, repository);
backup_engine.backup()?; backup_engine.backup()?;
let restore_target = tempdir()?; let restore_target = tempdir()?;
let restore_engine = restore::Engine::new(repository_path, &restore_target.path()); let restore_engine = restore::Engine::new(repository, &restore_target.path());
restore_engine.restore_all()?; restore_engine.restore_all()?;
let are_source_and_target_different = is_different(source_path, &restore_target.path()).unwrap(); let are_source_and_target_different = is_different(source_path, &restore_target.path()).unwrap();