use Repository states

This commit is contained in:
Alexander Weiss 2023-06-29 23:42:42 +02:00
parent fc90098670
commit d3f2e73dc8
27 changed files with 300 additions and 189 deletions

View File

@ -7,9 +7,11 @@ fn main() {
let _ = SimpleLogger::init(LevelFilter::Info, Config::default());
// Open repository
let mut repo_opts = RepositoryOptions::default();
repo_opts.repository = Some("/tmp/repo".to_string());
repo_opts.password = Some("test".to_string());
let repo_opts = RepositoryOptions {
repository: Some("/tmp/repo".to_string()),
password: Some("test".to_string()),
..Default::default()
};
let repo = Repository::new(&repo_opts).unwrap().open().unwrap();
// Check respository with standard options

View File

@ -7,9 +7,11 @@ fn main() {
let _ = SimpleLogger::init(LevelFilter::Info, Config::default());
// Open repository
let mut repo_opts = RepositoryOptions::default();
repo_opts.repository = Some("/tmp/repo".to_string());
repo_opts.password = Some("test".to_string());
let repo_opts = RepositoryOptions {
repository: Some("/tmp/repo".to_string()),
password: Some("test".to_string()),
..Default::default()
};
let repo = Repository::new(&repo_opts).unwrap().open().unwrap();
// Check respository with standard options

View File

@ -7,9 +7,11 @@ fn main() {
let _ = SimpleLogger::init(LevelFilter::Info, Config::default());
// Open repository
let mut repo_opts = RepositoryOptions::default();
repo_opts.repository = Some("/tmp/repo".to_string());
repo_opts.password = Some("test".to_string());
let repo_opts = RepositoryOptions {
repository: Some("/tmp/repo".to_string()),
password: Some("test".to_string()),
..Default::default()
};
let repo = Repository::new(&repo_opts).unwrap().open().unwrap();
let prune_opts = PruneOpts::default();

View File

@ -3,40 +3,49 @@ use std::path::Path;
use bytes::Bytes;
use crate::{
error::CommandErrorKind, repository::IndexedRepository, BlobType, DecryptReadBackend, FileType,
Id, IndexedBackend, OpenRepository, ProgressBars, ReadBackend, RusticResult, SnapshotFile,
Tree,
error::CommandErrorKind,
repository::{Indexed, Open, Repository},
BlobType, DecryptReadBackend, FileType, Id, IndexedBackend, ProgressBars, ReadBackend,
RusticResult, SnapshotFile, Tree,
};
pub fn cat_file<P>(repo: &OpenRepository<P>, tpe: FileType, id: &str) -> RusticResult<Bytes> {
let id = repo.dbe.find_id(tpe, id)?;
let data = repo.dbe.read_encrypted_full(tpe, &id)?;
pub(crate) fn cat_file<P, S: Open>(
repo: &Repository<P, S>,
tpe: FileType,
id: &str,
) -> RusticResult<Bytes> {
let id = repo.dbe().find_id(tpe, id)?;
let data = repo.dbe().read_encrypted_full(tpe, &id)?;
Ok(data)
}
pub fn cat_blob<P>(repo: &IndexedRepository<P>, tpe: BlobType, id: &str) -> RusticResult<Bytes> {
pub(crate) fn cat_blob<P, S: Indexed>(
repo: &Repository<P, S>,
tpe: BlobType,
id: &str,
) -> RusticResult<Bytes> {
let id = Id::from_hex(id)?;
let data = repo.index.blob_from_backend(tpe, &id)?;
let data = repo.index().blob_from_backend(tpe, &id)?;
Ok(data)
}
pub fn cat_tree<P: ProgressBars>(
repo: &IndexedRepository<P>,
pub(crate) fn cat_tree<P: ProgressBars, S: Indexed>(
repo: &Repository<P, S>,
snap: &str,
sn_filter: impl FnMut(&SnapshotFile) -> bool + Send + Sync,
) -> RusticResult<Bytes> {
let (id, path) = snap.split_once(':').unwrap_or((snap, ""));
let snap = SnapshotFile::from_str(
&repo.repo.dbe,
repo.dbe(),
id,
sn_filter,
&repo.repo.pb.progress_counter("getting snapshot..."),
&repo.pb.progress_counter("getting snapshot..."),
)?;
let node = Tree::node_from_path(&repo.index, snap.tree, Path::new(path))?;
let node = Tree::node_from_path(repo.index(), snap.tree, Path::new(path))?;
let id = node
.subtree
.ok_or_else(|| CommandErrorKind::PathIsNoDir(path.to_string()))?;
let data = repo.index.blob_from_backend(BlobType::Tree, &id)?;
let data = repo.index().blob_from_backend(BlobType::Tree, &id)?;
Ok(data)
}

View File

@ -8,10 +8,12 @@ use rayon::prelude::{IntoParallelIterator, ParallelBridge, ParallelIterator};
use zstd::stream::decode_all;
use crate::{
hash, progress::ProgressBars, BlobType, Cache, DecryptReadBackend, FileType, Id, IndexBackend,
IndexCollector, IndexFile, IndexPack, IndexType, IndexedBackend, NodeType, OpenRepository,
PackHeader, PackHeaderLength, PackHeaderRef, Progress, ReadBackend, RusticResult, SnapshotFile,
TreeStreamerOnce,
hash,
progress::ProgressBars,
repository::{Open, Repository},
BlobType, Cache, DecryptReadBackend, FileType, Id, IndexBackend, IndexCollector, IndexFile,
IndexPack, IndexType, IndexedBackend, NodeType, PackHeader, PackHeaderLength, PackHeaderRef,
Progress, ReadBackend, RusticResult, SnapshotFile, TreeStreamerOnce,
};
/// `check` subcommand
@ -28,9 +30,9 @@ pub struct CheckOpts {
}
impl CheckOpts {
pub fn run<P: ProgressBars>(self, repo: &OpenRepository<P>) -> RusticResult<()> {
let be = &repo.dbe;
let cache = &repo.cache;
pub(crate) fn run<P: ProgressBars, S: Open>(self, repo: &Repository<P, S>) -> RusticResult<()> {
let be = repo.dbe();
let cache = repo.cache();
let hot_be = &repo.be_hot;
let raw_be = &repo.be;
let pb = &repo.pb;

View File

@ -1,12 +1,13 @@
use std::io::Write;
use crate::{
error::CommandErrorKind, repository::IndexedRepository, BlobType, IndexedBackend, Node,
NodeType, RusticResult,
error::CommandErrorKind,
repository::{Indexed, Repository},
BlobType, IndexedBackend, Node, NodeType, RusticResult,
};
pub(crate) fn dump<P>(
repo: &IndexedRepository<P>,
pub(crate) fn dump<P, S: Indexed>(
repo: &Repository<P, S>,
node: &Node,
w: &mut impl Write,
) -> RusticResult<()> {
@ -16,7 +17,7 @@ pub(crate) fn dump<P>(
for id in node.content.as_ref().unwrap() {
// TODO: cache blobs which are needed later
let data = repo.index.blob_from_backend(BlobType::Data, id)?;
let data = repo.index().blob_from_backend(BlobType::Data, id)?;
w.write_all(&data)?;
}
Ok(())

View File

@ -6,7 +6,7 @@ use serde::Deserialize;
use serde_with::{serde_as, DisplayFromStr};
use crate::{
Id, OpenRepository, ProgressBars, RusticResult, SnapshotFile, SnapshotGroup,
repository::Open, Id, ProgressBars, Repository, RusticResult, SnapshotFile, SnapshotGroup,
SnapshotGroupCriterion, StringList,
};
@ -41,8 +41,8 @@ impl ForgetGroups {
}
}
pub(crate) fn get_forget_snapshots<P: ProgressBars>(
repo: &OpenRepository<P>,
pub(crate) fn get_forget_snapshots<P: ProgressBars, S: Open>(
repo: &Repository<P, S>,
keep: &KeepOptions,
group_by: SnapshotGroupCriterion,
filter: impl FnMut(&SnapshotFile) -> bool,

View File

@ -19,11 +19,11 @@ use itertools::Itertools;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use crate::{
error::CommandErrorKind, BlobType, BlobTypeMap, DecryptReadBackend, DecryptWriteBackend,
FileType, HeaderEntry, Id, IndexBackend, IndexBlob, IndexCollector, IndexFile, IndexPack,
IndexType, IndexedBackend, Indexer, Initialize, NodeType, OpenRepository, PackSizer, Progress,
ProgressBars, ReadBackend, ReadIndex, Repacker, RusticResult, SnapshotFile, Sum,
TreeStreamerOnce,
error::CommandErrorKind, repository::Open, BlobType, BlobTypeMap, DecryptReadBackend,
DecryptWriteBackend, FileType, HeaderEntry, Id, IndexBackend, IndexBlob, IndexCollector,
IndexFile, IndexPack, IndexType, IndexedBackend, Indexer, Initialize, NodeType, PackSizer,
Progress, ProgressBars, ReadBackend, ReadIndex, Repacker, Repository, RusticResult,
SnapshotFile, Sum, TreeStreamerOnce,
};
pub(super) mod constants {
@ -115,11 +115,14 @@ impl Default for PruneOpts {
}
impl PruneOpts {
pub fn get_plan<P: ProgressBars>(&self, repo: &OpenRepository<P>) -> RusticResult<PrunePlan> {
pub fn get_plan<P: ProgressBars, S: Open>(
&self,
repo: &Repository<P, S>,
) -> RusticResult<PrunePlan> {
let pb = &repo.pb;
let be = &repo.dbe;
let be = repo.dbe();
if repo.config.version < 2 && self.repack_uncompressed {
if repo.config().version < 2 && self.repack_uncompressed {
return Err(CommandErrorKind::RepackUncompressedRepoV1.into());
}
@ -158,9 +161,9 @@ impl PruneOpts {
pruner.check()?;
let repack_cacheable_only = self
.repack_cacheable_only
.unwrap_or_else(|| repo.config.is_hot == Some(true));
.unwrap_or_else(|| repo.config().is_hot == Some(true));
let pack_sizer =
total_size.map(|tpe, size| PackSizer::from_config(&repo.config, tpe, size));
total_size.map(|tpe, size| PackSizer::from_config(repo.config(), tpe, size));
pruner.decide_packs(
Duration::from_std(*self.keep_pack).map_err(CommandErrorKind::FromOutOfRangeError)?,
Duration::from_std(*self.keep_delete).map_err(CommandErrorKind::FromOutOfRangeError)?,
@ -745,13 +748,13 @@ impl PrunePlan {
}
#[allow(clippy::significant_drop_tightening)]
pub fn do_prune<P: ProgressBars>(
pub fn do_prune<P: ProgressBars, S: Open>(
self,
repo: &OpenRepository<P>,
repo: &Repository<P, S>,
opts: &PruneOpts,
) -> RusticResult<()> {
repo.warm_up_wait(self.repack_packs().into_iter())?;
let be = &repo.dbe;
let be = repo.dbe();
let pb = &repo.pb;
let indexer = Indexer::new_unindexed(be.clone()).into_shared();
@ -776,7 +779,7 @@ impl PrunePlan {
be.clone(),
BlobType::Tree,
indexer.clone(),
&repo.config,
repo.config(),
size_after_prune[BlobType::Tree],
)?;
@ -784,7 +787,7 @@ impl PrunePlan {
be.clone(),
BlobType::Data,
indexer.clone(),
&repo.config,
repo.config(),
size_after_prune[BlobType::Data],
)?;
@ -1050,7 +1053,7 @@ impl PackInfo {
// find used blobs in repo
fn find_used_blobs(
index: &(impl IndexedBackend + Unpin),
index: &impl IndexedBackend,
ignore_snaps: &[Id],
pb: &impl ProgressBars,
) -> RusticResult<HashMap<Id, u8>> {

View File

@ -3,8 +3,9 @@ use serde::{Deserialize, Serialize};
use crate::{
index::IndexEntry,
repofile::indexfile::{IndexFile, IndexPack},
BlobType, BlobTypeMap, DecryptReadBackend, FileType, OpenRepository, Progress, ProgressBars,
ReadBackend, Repository, RusticResult, ALL_FILE_TYPES,
repository::Open,
BlobType, BlobTypeMap, DecryptReadBackend, FileType, Progress, ProgressBars, ReadBackend,
Repository, RusticResult, ALL_FILE_TYPES,
};
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
@ -53,8 +54,8 @@ impl PackInfo {
}
}
pub(crate) fn collect_index_infos<P: ProgressBars>(
repo: &OpenRepository<P>,
pub(crate) fn collect_index_infos<P: ProgressBars, S: Open>(
repo: &Repository<P, S>,
) -> RusticResult<IndexInfos> {
let mut blob_info = BlobTypeMap::<()>::default().map(|blob_type, _| BlobInfo {
blob_type,
@ -72,7 +73,7 @@ pub(crate) fn collect_index_infos<P: ProgressBars>(
let mut pack_info_delete = pack_info;
let p = repo.pb.progress_counter("scanning index...");
for index in repo.dbe.stream_all::<IndexFile>(&p)? {
for index in repo.dbe().stream_all::<IndexFile>(&p)? {
let index = index?.1;
for pack in &index.packs {
let tpe = pack.blob_type();
@ -130,7 +131,9 @@ pub(crate) fn collect_file_info(be: &impl ReadBackend) -> RusticResult<Vec<RepoF
Ok(files)
}
pub fn collect_file_infos<P: ProgressBars>(repo: &Repository<P>) -> RusticResult<RepoFileInfos> {
pub fn collect_file_infos<P: ProgressBars, S>(
repo: &Repository<P, S>,
) -> RusticResult<RepoFileInfos> {
let p = repo.pb.progress_spinner("scanning files...");
let files = collect_file_info(&repo.be)?;
let files_hot = repo.be_hot.as_ref().map(collect_file_info).transpose()?;

View File

@ -1,35 +1,35 @@
//! `smapshot` subcommand
use crate::{
OpenRepository, ProgressBars, RusticResult, SnapshotFile, SnapshotGroup, SnapshotGroupCriterion,
repository::Open, ProgressBars, Repository, RusticResult, SnapshotFile, SnapshotGroup,
SnapshotGroupCriterion,
};
pub(crate) fn get_snapshot_group<P: ProgressBars>(
repo: &OpenRepository<P>,
pub(crate) fn get_snapshot_group<P: ProgressBars, S: Open>(
repo: &Repository<P, S>,
ids: &[String],
group_by: SnapshotGroupCriterion,
filter: impl FnMut(&SnapshotFile) -> bool,
) -> RusticResult<Vec<(SnapshotGroup, Vec<SnapshotFile>)>> {
let pb = &repo.pb;
let dbe = repo.dbe();
let p = pb.progress_counter("getting snapshots...");
let groups = match ids {
[] => SnapshotFile::group_from_backend(&repo.dbe, filter, group_by, &p)?,
[id] if id == "latest" => {
SnapshotFile::group_from_backend(&repo.dbe, filter, group_by, &p)?
.into_iter()
.map(|(group, mut snaps)| {
snaps.sort_unstable();
let last_idx = snaps.len() - 1;
snaps.swap(0, last_idx);
snaps.truncate(1);
(group, snaps)
})
.collect::<Vec<_>>()
}
[] => SnapshotFile::group_from_backend(dbe, filter, group_by, &p)?,
[id] if id == "latest" => SnapshotFile::group_from_backend(dbe, filter, group_by, &p)?
.into_iter()
.map(|(group, mut snaps)| {
snaps.sort_unstable();
let last_idx = snaps.len() - 1;
snaps.swap(0, last_idx);
snaps.truncate(1);
(group, snaps)
})
.collect::<Vec<_>>(),
_ => {
let item = (
SnapshotGroup::default(),
SnapshotFile::from_ids(&repo.dbe, ids, &p)?,
SnapshotFile::from_ids(dbe, ids, &p)?,
);
vec![item]
}

View File

@ -147,5 +147,5 @@ pub use crate::{
},
RepoFile,
},
repository::{read_password_from_reader, OpenRepository, Repository, RepositoryOptions},
repository::{read_password_from_reader, Open, OpenStatus, Repository, RepositoryOptions},
};

View File

@ -37,8 +37,9 @@ use crate::{
crypto::aespoly1305::Key,
error::RepositoryErrorKind,
repofile::{configfile::ConfigFile, keyfile::find_key_in_backend},
BlobType, Id, IndexBackend, NoProgressBars, Node, ProgressBars, PruneOpts, PrunePlan,
RusticResult, SnapshotFile, SnapshotGroup, SnapshotGroupCriterion, Tree,
BlobType, DecryptFullBackend, Id, IndexBackend, IndexedBackend, NoProgressBars, Node,
ProgressBars, PruneOpts, PrunePlan, RusticResult, SnapshotFile, SnapshotGroup,
SnapshotGroupCriterion, Tree,
};
pub(super) mod constants {
@ -176,21 +177,22 @@ pub fn read_password_from_reader(file: &mut impl BufRead) -> RusticResult<String
}
#[derive(Debug)]
pub struct Repository<P> {
pub struct Repository<P, S> {
name: String,
pub be: HotColdBackend<ChooseBackend>,
pub be_hot: Option<ChooseBackend>,
opts: RepositoryOptions,
pub(crate) pb: P,
status: S,
}
impl Repository<NoProgressBars> {
impl Repository<NoProgressBars, ()> {
pub fn new(opts: &RepositoryOptions) -> RusticResult<Self> {
Self::new_with_progress(opts, NoProgressBars {})
}
}
impl<P> Repository<P> {
impl<P> Repository<P, ()> {
pub fn new_with_progress(opts: &RepositoryOptions, pb: P) -> RusticResult<Self> {
let be = match &opts.repository {
Some(repo) => ChooseBackend::from_url(repo)?,
@ -226,6 +228,7 @@ impl<P> Repository<P> {
be_hot,
opts: opts.clone(),
pb,
status: (),
})
}
@ -276,11 +279,11 @@ impl<P> Repository<P> {
}
}
pub fn open(self) -> RusticResult<OpenRepository<P>> {
let config_ids = match self.be.list(FileType::Config) {
Ok(val) => val,
Err(_e) => return Err(RepositoryErrorKind::ListingRepositoryConfigFileFailed.into()),
};
pub fn open(self) -> RusticResult<Repository<P, OpenStatus>> {
let config_ids = self
.be
.list(FileType::Config)
.map_err(|_| RepositoryErrorKind::ListingRepositoryConfigFileFailed)?;
match config_ids.len() {
1 => {} // ok, continue
@ -320,24 +323,35 @@ impl<P> Repository<P> {
let zstd = config.zstd()?;
dbe.set_zstd(zstd);
Ok(OpenRepository {
name: self.name,
let open = OpenStatus {
key,
dbe,
cache,
config,
};
Ok(Repository {
name: self.name,
be: self.be,
be_hot: self.be_hot,
config,
opts: self.opts,
pb: self.pb,
status: open,
})
}
}
impl<P: ProgressBars> Repository<P> {
impl<P: ProgressBars, S> Repository<P, S> {
pub fn infos_files(&self) -> RusticResult<RepoFileInfos> {
commands::repoinfo::collect_file_infos(self)
}
pub fn warm_up(&self, packs: impl ExactSizeIterator<Item = Id>) -> RusticResult<()> {
warm_up(self, packs)
}
pub fn warm_up_wait(&self, packs: impl ExactSizeIterator<Item = Id>) -> RusticResult<()> {
warm_up_wait(self, packs)
}
}
pub(crate) fn get_key(be: &impl ReadBackend, password: Option<String>) -> RusticResult<Key> {
@ -366,20 +380,56 @@ pub(crate) fn get_key(be: &impl ReadBackend, password: Option<String>) -> Rustic
Err(RepositoryErrorKind::IncorrectPassword.into())
}
#[derive(Debug)]
pub struct OpenRepository<P> {
pub name: String,
pub be: HotColdBackend<ChooseBackend>,
pub be_hot: Option<ChooseBackend>,
pub key: Key,
pub cache: Option<Cache>,
pub dbe: DecryptBackend<CachedBackend<HotColdBackend<ChooseBackend>>, Key>,
pub config: ConfigFile,
pub opts: RepositoryOptions,
pub(crate) pb: P,
pub trait Open {
type DBE: DecryptFullBackend;
fn key(&self) -> &Key;
fn cache(&self) -> Option<&Cache>;
fn dbe(&self) -> &Self::DBE;
fn config(&self) -> &ConfigFile;
}
impl<P: ProgressBars> OpenRepository<P> {
impl<P, S: Open> Open for Repository<P, S> {
type DBE = S::DBE;
fn key(&self) -> &Key {
self.status.key()
}
fn cache(&self) -> Option<&Cache> {
self.status.cache()
}
fn dbe(&self) -> &Self::DBE {
self.status.dbe()
}
fn config(&self) -> &ConfigFile {
self.status.config()
}
}
#[derive(Debug)]
pub struct OpenStatus {
key: Key,
cache: Option<Cache>,
dbe: DecryptBackend<CachedBackend<HotColdBackend<ChooseBackend>>, Key>,
config: ConfigFile,
}
impl Open for OpenStatus {
type DBE = DecryptBackend<CachedBackend<HotColdBackend<ChooseBackend>>, Key>;
fn key(&self) -> &Key {
&self.key
}
fn cache(&self) -> Option<&Cache> {
self.cache.as_ref()
}
fn dbe(&self) -> &Self::DBE {
&self.dbe
}
fn config(&self) -> &ConfigFile {
&self.config
}
}
impl<P: ProgressBars, S: Open> Repository<P, S> {
pub fn get_snapshot_group(
&self,
ids: &[String],
@ -391,7 +441,7 @@ impl<P: ProgressBars> OpenRepository<P> {
pub fn get_snapshots(&self, ids: &[String]) -> RusticResult<Vec<SnapshotFile>> {
let p = self.pb.progress_counter("getting snapshots...");
SnapshotFile::from_ids(&self.dbe, ids, &p)
SnapshotFile::from_ids(self.dbe(), ids, &p)
}
pub fn get_forget_snapshots(
@ -405,7 +455,7 @@ impl<P: ProgressBars> OpenRepository<P> {
pub fn delete_snapshots(&self, ids: &[Id]) -> RusticResult<()> {
let p = self.pb.progress_counter("removing snapshots...");
self.dbe
self.dbe()
.delete_list(FileType::Snapshot, true, ids.iter(), p)?;
Ok(())
}
@ -422,32 +472,71 @@ impl<P: ProgressBars> OpenRepository<P> {
opts.get_plan(self)
}
pub fn to_indexed(self) -> RusticResult<IndexedRepository<P>> {
let index = IndexBackend::new(&self.dbe, &self.pb.progress_counter(""))?;
Ok(IndexedRepository { repo: self, index })
pub fn to_indexed(self) -> RusticResult<Repository<P, IndexedStatus<S>>> {
let index = IndexBackend::new(self.dbe(), &self.pb.progress_counter(""))?;
let status = IndexedStatus {
open: self.status,
index,
};
Ok(Repository {
name: self.name,
be: self.be,
be_hot: self.be_hot,
opts: self.opts,
pb: self.pb,
status,
})
}
pub fn infos_index(&self) -> RusticResult<IndexInfos> {
commands::repoinfo::collect_index_infos(self)
}
}
pub fn warm_up(&self, packs: impl ExactSizeIterator<Item = Id>) -> RusticResult<()> {
warm_up(self, packs)
}
pub trait Indexed: Open {
type I: IndexedBackend;
fn index(&self) -> &Self::I;
}
pub fn warm_up_wait(&self, packs: impl ExactSizeIterator<Item = Id>) -> RusticResult<()> {
warm_up_wait(self, packs)
impl<P, S: Indexed> Indexed for Repository<P, S> {
type I = S::I;
fn index(&self) -> &Self::I {
self.status.index()
}
}
#[derive(Debug)]
pub struct IndexedRepository<P> {
pub(crate) repo: OpenRepository<P>,
pub(crate) index:
IndexBackend<DecryptBackend<CachedBackend<HotColdBackend<ChooseBackend>>, Key>>,
pub struct IndexedStatus<S: Open> {
open: S,
index: IndexBackend<S::DBE>,
}
impl<P: ProgressBars> IndexedRepository<P> {
impl<S: Open> Indexed for IndexedStatus<S> {
type I = IndexBackend<S::DBE>;
fn index(&self) -> &Self::I {
&self.index
}
}
impl<S: Open> Open for IndexedStatus<S> {
type DBE = S::DBE;
fn key(&self) -> &Key {
self.open.key()
}
fn cache(&self) -> Option<&Cache> {
self.open.cache()
}
fn dbe(&self) -> &Self::DBE {
self.open.dbe()
}
fn config(&self) -> &ConfigFile {
self.open.config()
}
}
impl<P: ProgressBars, S: Indexed> Repository<P, S> {
pub fn node_from_snapshot_path(
&self,
snap_path: &str,
@ -455,10 +544,10 @@ impl<P: ProgressBars> IndexedRepository<P> {
) -> RusticResult<Node> {
let (id, path) = snap_path.split_once(':').unwrap_or((snap_path, ""));
let p = &self.repo.pb.progress_counter("getting snapshot...");
let snap = SnapshotFile::from_str(&self.repo.dbe, id, filter, p)?;
let p = &self.pb.progress_counter("getting snapshot...");
let snap = SnapshotFile::from_str(self.dbe(), id, filter, p)?;
Tree::node_from_path(&self.index, snap.tree, Path::new(path))
Tree::node_from_path(self.index(), snap.tree, Path::new(path))
}
pub fn cat_blob(&self, tpe: BlobType, id: &str) -> RusticResult<Bytes> {

View File

@ -6,7 +6,7 @@ use rayon::ThreadPoolBuilder;
use super::parse_command;
use crate::{
error::RepositoryErrorKind, FileType, Id, OpenRepository, Progress, ProgressBars, ReadBackend,
error::RepositoryErrorKind, FileType, Id, Progress, ProgressBars, ReadBackend, Repository,
RusticResult,
};
@ -14,8 +14,8 @@ pub(super) mod constants {
pub(super) const MAX_READER_THREADS_NUM: usize = 20;
}
pub(crate) fn warm_up_wait<P: ProgressBars>(
repo: &OpenRepository<P>,
pub(crate) fn warm_up_wait<P: ProgressBars, S>(
repo: &Repository<P, S>,
packs: impl ExactSizeIterator<Item = Id>,
) -> RusticResult<()> {
warm_up(repo, packs)?;
@ -27,8 +27,8 @@ pub(crate) fn warm_up_wait<P: ProgressBars>(
Ok(())
}
pub(crate) fn warm_up<P: ProgressBars>(
repo: &OpenRepository<P>,
pub(crate) fn warm_up<P: ProgressBars, S>(
repo: &Repository<P, S>,
packs: impl ExactSizeIterator<Item = Id>,
) -> RusticResult<()> {
if let Some(command) = &repo.opts.warm_up_command {
@ -61,8 +61,8 @@ fn warm_up_command<P: ProgressBars>(
Ok(())
}
fn warm_up_access<P: ProgressBars>(
repo: &OpenRepository<P>,
fn warm_up_access<P: ProgressBars, S>(
repo: &Repository<P, S>,
packs: impl ExactSizeIterator<Item = Id>,
) -> RusticResult<()> {
let mut be = repo.be.clone();

View File

@ -41,7 +41,7 @@ use crate::{
use abscissa_core::{
config::Override, status_err, Command, Configurable, FrameworkError, Runnable, Shutdown,
};
use rustic_core::{OpenRepository, Repository};
use rustic_core::{OpenStatus, Repository};
/// Rustic Subcommands
/// Subcommands need to be listed in an enum.
@ -167,7 +167,7 @@ impl Configurable<RusticConfig> for EntryPoint {
}
}
fn open_repository<P>(repo: Repository<P>) -> OpenRepository<P> {
fn open_repository<P>(repo: Repository<P, ()>) -> Repository<P, OpenStatus> {
match repo.open() {
Ok(it) => it,
Err(err) => {
@ -177,7 +177,7 @@ fn open_repository<P>(repo: Repository<P>) -> OpenRepository<P> {
}
}
fn get_repository(config: &Arc<RusticConfig>) -> Repository<ProgressOptions> {
fn get_repository(config: &Arc<RusticConfig>) -> Repository<ProgressOptions, ()> {
let po = config.global.progress_options;
match Repository::new_with_progress(&config.repository, po) {
Ok(it) => it,

View File

@ -22,7 +22,7 @@ use serde::Deserialize;
use rustic_core::{
Archiver, DryRunBackend, IndexBackend, LocalSource, LocalSourceFilterOptions,
LocalSourceSaveOptions, PathList, ProgressBars, SnapshotFile, SnapshotGroup,
LocalSourceSaveOptions, Open, PathList, ProgressBars, SnapshotFile, SnapshotGroup,
SnapshotGroupCriterion, SnapshotOptions, StdinSource,
};
@ -196,7 +196,7 @@ impl BackupCmd {
};
let index =
IndexBackend::only_full_trees(&repo.dbe, &progress_options.progress_counter(""))?;
IndexBackend::only_full_trees(repo.dbe(), &progress_options.progress_counter(""))?;
for source in sources {
let mut opts = self.clone();
@ -230,7 +230,7 @@ impl BackupCmd {
// merge "backup" section from config file, if given
opts.merge(config.backup.clone());
let be = DryRunBackend::new(repo.dbe.clone(), config.global.dry_run);
let be = DryRunBackend::new(repo.dbe().clone(), config.global.dry_run);
info!("starting to backup {source}...");
let as_path = opts.as_path.map(|p| {
match p.parse_dot() {
@ -283,7 +283,7 @@ impl BackupCmd {
let archiver = Archiver::new(
be,
index,
&repo.config,
repo.config(),
parent_tree,
opts.ignore_ctime,
opts.ignore_inode,

View File

@ -12,7 +12,7 @@ use abscissa_core::{Command, Runnable, Shutdown};
use anyhow::{bail, Result};
use bytesize::ByteSize;
use rustic_core::{ConfigFile, DecryptBackend, DecryptWriteBackend};
use rustic_core::{ConfigFile, DecryptBackend, DecryptWriteBackend, Open};
/// `config` subcommand
#[derive(clap::Parser, Command, Debug)]
@ -33,23 +33,24 @@ impl Runnable for ConfigCmd {
impl ConfigCmd {
fn inner_run(&self) -> Result<()> {
let config = RUSTIC_APP.config();
let mut repo = open_repository(get_repository(&config));
let repo = open_repository(get_repository(&config));
let mut new_config = repo.config.clone();
let mut new_config = repo.config().clone();
self.config_opts.apply(&mut new_config)?;
if new_config == repo.config {
if &new_config == repo.config() {
println!("config is unchanged");
} else {
new_config.is_hot = None;
// don't compress the config file
repo.dbe.set_zstd(None);
let mut dbe = repo.dbe().clone();
dbe.set_zstd(None);
// for hot/cold backend, this only saves the config to the cold repo.
_ = repo.dbe.save_file(&new_config)?;
_ = dbe.save_file(&new_config)?;
if let Some(hot_be) = repo.be_hot {
if let Some(hot_be) = repo.be_hot.clone() {
// save config to hot repo
let mut dbe = DecryptBackend::new(&hot_be, repo.key);
let mut dbe = DecryptBackend::new(&hot_be, *repo.key());
// don't compress the config file
dbe.set_zstd(None);
new_config.is_hot = Some(true);

View File

@ -16,7 +16,7 @@ use serde::Deserialize;
use crate::commands::key::KeyOpts;
use rustic_core::{
FileType, Id, IndexBackend, ProgressBars, ReadBackend, Repository, RepositoryOptions,
FileType, Id, IndexBackend, Open, ProgressBars, ReadBackend, Repository, RepositoryOptions,
SnapshotFile,
};
@ -61,7 +61,7 @@ impl CopyCmd {
RUSTIC_APP.shutdown(Shutdown::Crash);
}
let be = &repo.dbe;
let be = repo.dbe();
let p = config.global.progress_options.progress_hidden();
let mut snapshots = if self.ids.is_empty() {
SnapshotFile::all_from_backend(be, |sn| config.snapshot_filter.matches(sn), &p)?
@ -71,16 +71,15 @@ impl CopyCmd {
// sort for nicer output
snapshots.sort_unstable();
let be = &repo.dbe;
let index = IndexBackend::new(be, &config.global.progress_options.progress_counter(""))?;
let poly = repo.config.poly()?;
let poly = repo.config().poly()?;
for target_opt in &config.copy.targets {
let repo_dest = Repository::new(target_opt)?;
if self.init && repo_dest.be.list(FileType::Config)?.is_empty() {
let mut config_dest = repo.config.clone();
let mut config_dest = repo.config().clone();
config_dest.id = Id::random();
save_config(
config_dest,
@ -92,8 +91,8 @@ impl CopyCmd {
}
let repo_dest = repo_dest.open()?;
info!("copying to target {}...", repo_dest.name);
if poly != repo_dest.config.poly()? {
info!("copying to target {:?}...", repo_dest); // TODO: repo_dest.name
if poly != repo_dest.config().poly()? {
bail!("cannot copy to repository with different chunker parameter (re-chunking not implemented)!");
}
copy(&snapshots, &index, &repo_dest)?;

View File

@ -16,7 +16,7 @@ use anyhow::{anyhow, bail, Context, Result};
use rustic_core::{
hash, IndexBackend, LocalDestination, LocalSource, LocalSourceFilterOptions,
LocalSourceSaveOptions, Node, NodeStreamer, NodeType, Progress, ProgressBars, ReadIndex,
LocalSourceSaveOptions, Node, NodeStreamer, NodeType, Open, Progress, ProgressBars, ReadIndex,
ReadSourceEntry, RusticResult, SnapshotFile, Tree,
};
@ -58,7 +58,7 @@ impl DiffCmd {
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let be = repo.dbe();
let (id1, path1) = arg_to_snap_path(&self.snap1, "");
let (id2, path2) = arg_to_snap_path(&self.snap2, path1);

View File

@ -14,7 +14,7 @@ use std::{fs::File, io::BufReader};
use dialoguer::Password;
use rustic_core::{hash, read_password_from_reader, FileType, KeyFile, WriteBackend};
use rustic_core::{hash, read_password_from_reader, FileType, KeyFile, Open, WriteBackend};
/// `key` subcommand
#[derive(clap::Parser, Command, Debug)]
@ -75,8 +75,8 @@ impl AddCmd {
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let key = repo.key;
let be = repo.dbe();
let key = repo.key();
let pass = self.new_password_file.as_ref().map_or_else(
|| match Password::new()
@ -109,7 +109,7 @@ impl AddCmd {
},
);
let ko = self.key_opts.clone();
let keyfile = KeyFile::generate(key, &pass, ko.hostname, ko.username, ko.with_created)?;
let keyfile = KeyFile::generate(*key, &pass, ko.hostname, ko.username, ko.with_created)?;
let data = serde_json::to_vec(&keyfile)?;
let id = hash(&data);
be.write_bytes(FileType::Key, &id, false, data.into())?;

View File

@ -11,7 +11,7 @@ use abscissa_core::{Command, Runnable, Shutdown};
use anyhow::{bail, Result};
use rustic_core::{DecryptReadBackend, FileType, IndexFile, ProgressBars, ReadBackend};
use rustic_core::{DecryptReadBackend, FileType, IndexFile, Open, ProgressBars, ReadBackend};
/// `list` subcommand
#[derive(clap::Parser, Command, Debug)]
@ -39,7 +39,7 @@ impl ListCmd {
let tpe = match self.tpe.as_str() {
// special treatment for listing blobs: read the index and display it
"blobs" => {
repo.dbe
repo.dbe()
.stream_all::<IndexFile>(&config.global.progress_options.progress_hidden())?
.into_iter()
.for_each(|index| {

View File

@ -13,7 +13,7 @@ use anyhow::Result;
use std::path::Path;
use rustic_core::{
IndexBackend, NodeStreamer, ProgressBars, SnapshotFile, Tree, TreeStreamerOptions,
IndexBackend, NodeStreamer, Open, ProgressBars, SnapshotFile, Tree, TreeStreamerOptions,
};
/// `ls` subcommand
@ -47,7 +47,7 @@ impl LsCmd {
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let be = repo.dbe();
let mut recursive = self.recursive;
let (id, path) = self.snap.split_once(':').unwrap_or_else(|| {

View File

@ -13,8 +13,8 @@ use log::info;
use chrono::Local;
use rustic_core::{
merge_trees, BlobType, DecryptWriteBackend, FileType, Id, IndexBackend, Indexer, Node, Packer,
PathList, Progress, ProgressBars, ReadIndex, SnapshotFile, SnapshotOptions, Tree,
merge_trees, BlobType, DecryptWriteBackend, FileType, Id, IndexBackend, Indexer, Node, Open,
Packer, PathList, Progress, ProgressBars, ReadIndex, SnapshotFile, SnapshotOptions, Tree,
};
/// `merge` subcommand
@ -59,7 +59,7 @@ impl MergeCmd {
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let be = repo.dbe();
let p = progress_options.progress_hidden();
let snapshots = if self.ids.is_empty() {
@ -75,7 +75,7 @@ impl MergeCmd {
be.clone(),
BlobType::Tree,
indexer.clone(),
&repo.config,
repo.config(),
index.total_size(BlobType::Tree),
)?;

View File

@ -15,8 +15,8 @@ use anyhow::Result;
use rustic_core::{
BlobType, DecryptReadBackend, DecryptWriteBackend, FileType, Id, IndexBackend, IndexFile,
IndexPack, IndexedBackend, Indexer, NodeType, PackHeader, PackHeaderRef, Packer, Progress,
ProgressBars, ReadBackend, ReadIndex, SnapshotFile, StringList, Tree, WriteBackend,
IndexPack, IndexedBackend, Indexer, NodeType, Open, PackHeader, PackHeaderRef, Packer,
Progress, ProgressBars, ReadBackend, ReadIndex, SnapshotFile, StringList, Tree, WriteBackend,
};
/// `repair` subcommand
@ -89,7 +89,7 @@ impl IndexSubCmd {
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let be = repo.dbe();
let p = progress_options.progress_spinner("listing packs...");
let mut packs: HashMap<_, _> = be.list_with_size(FileType::Pack)?.into_iter().collect();
p.finish();
@ -237,8 +237,8 @@ impl SnapSubCmd {
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let config_file = &repo.config;
let be = repo.dbe();
let config_file = repo.config();
let p = progress_options.progress_hidden();
let snapshots = if self.ids.is_empty() {

View File

@ -29,7 +29,7 @@ use rayon::ThreadPoolBuilder;
use rustic_core::{
hash, DecryptReadBackend, FileType, Id, IndexBackend, IndexedBackend, LocalDestination, Node,
NodeStreamer, NodeType, Progress, ProgressBars, RestoreStats, SnapshotFile, Tree,
NodeStreamer, NodeType, Open, Progress, ProgressBars, RestoreStats, SnapshotFile, Tree,
TreeStreamerOptions,
};
@ -91,7 +91,7 @@ impl RestoreCmd {
let config = RUSTIC_APP.config();
let progress_options = &config.global.progress_options;
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let be = repo.dbe();
let (id, path) = self.snap.split_once(':').unwrap_or((&self.snap, ""));
let snap = SnapshotFile::from_str(

View File

@ -12,7 +12,7 @@ use abscissa_core::{Command, Runnable, Shutdown};
use chrono::{Duration, Local};
use rustic_core::{
DecryptWriteBackend, DeleteOption, FileType, Id, ProgressBars, SnapshotFile, StringList,
DecryptWriteBackend, DeleteOption, FileType, Id, Open, ProgressBars, SnapshotFile, StringList,
};
/// `tag` subcommand
@ -81,7 +81,7 @@ impl TagCmd {
let config = RUSTIC_APP.config();
let repo = open_repository(get_repository(&config));
let be = &repo.dbe;
let be = repo.dbe();
let p = config.global.progress_options.progress_hidden();
let snapshots = if self.ids.is_empty() {

View File

@ -131,13 +131,11 @@ fn get_config_paths(filename: &str) -> Vec<PathBuf> {
#[cfg(target_os = "windows")]
fn get_global_config_path() -> Option<PathBuf> {
if let Some(program_data) = std::env::var_os("PROGRAMDATA") {
std::env::var_os("PROGRAMDATA").map(|program_data| {
let mut path = PathBuf::from(program_data);
path.push(r"rustic\config");
Some(path)
} else {
None
}
path
})
}
#[cfg(any(target_os = "ios", target_arch = "wasm32"))]

View File

@ -11,19 +11,19 @@ use log::{info, trace};
use rayon::prelude::{IntoParallelRefIterator, ParallelBridge, ParallelIterator};
use rustic_core::{
BlobType, DecryptWriteBackend, IndexBackend, IndexedBackend, Indexer, NodeType, OpenRepository,
Packer, Progress, ProgressBars, ReadIndex, SnapshotFile, TreeStreamerOnce,
BlobType, DecryptWriteBackend, IndexBackend, IndexedBackend, Indexer, NodeType, Open, Packer,
Progress, ProgressBars, ReadIndex, Repository, SnapshotFile, TreeStreamerOnce,
};
use crate::application::RUSTIC_APP;
pub(crate) fn copy<P>(
pub(crate) fn copy<P, S: Open>(
snapshots: &[SnapshotFile],
index: &impl IndexedBackend,
repo_dest: &OpenRepository<P>,
repo_dest: &Repository<P, S>,
) -> Result<()> {
let config = RUSTIC_APP.config();
let be_dest = &repo_dest.dbe;
let be_dest = repo_dest.dbe();
let progress_options = &config.global.progress_options;
let snapshots = relevant_snapshots(
@ -54,14 +54,14 @@ pub(crate) fn copy<P>(
be_dest.clone(),
BlobType::Data,
indexer.clone(),
&repo_dest.config,
repo_dest.config(),
index.total_size(BlobType::Data),
)?;
let tree_packer = Packer::new(
be_dest.clone(),
BlobType::Tree,
indexer.clone(),
&repo_dest.config,
repo_dest.config(),
index.total_size(BlobType::Tree),
)?;
@ -121,9 +121,9 @@ pub(crate) fn copy<P>(
Ok(())
}
pub(crate) fn relevant_snapshots<F, P>(
pub(crate) fn relevant_snapshots<F, P, S: Open>(
snaps: &[SnapshotFile],
dest_repo: &OpenRepository<P>,
dest_repo: &Repository<P, S>,
filter: F,
p: &impl Progress,
) -> Result<Vec<SnapshotFile>>
@ -131,7 +131,7 @@ where
F: FnMut(&SnapshotFile) -> bool,
{
// save snapshots in destination in BTreeSet, as we want to efficiently search within to filter out already existing snapshots before copying.
let snapshots_dest: BTreeSet<_> = SnapshotFile::all_from_backend(&dest_repo.dbe, filter, p)?
let snapshots_dest: BTreeSet<_> = SnapshotFile::all_from_backend(dest_repo.dbe(), filter, p)?
.into_iter()
.map(SnapshotFile::clear_ids)
.collect();