use crate::chunker::AsyncChunker;
use crate::manifest::archive::{ActiveArchive, Extent};
use crate::manifest::target::{BackupObject, BackupTarget, RestoreObject, RestoreTarget};
use crate::repository::{BackendClone, Repository};
use asuran_core::manifest::listing::Node;
use async_trait::async_trait;
use thiserror::Error;
use std::collections::HashMap;
use std::io::{Read, Write};
#[derive(Error, Debug)]
#[non_exhaustive]
pub enum DriverError {
#[error("")]
ArchiveError(#[from] crate::manifest::archive::ArchiveError),
}
type Result<T> = std::result::Result<T, DriverError>;
#[async_trait]
pub trait BackupDriver<T: Read + Send + 'static>: BackupTarget<T> {
async fn raw_store_object<B: BackendClone, C: AsyncChunker + Send + 'static>(
&self,
repo: &mut Repository<B>,
chunker: C,
archive: &ActiveArchive,
node: Node,
objects: HashMap<String, BackupObject<T>>,
) -> Result<()> {
if node.is_file() {
for (namespace, backup_object) in objects {
let path = &node.path;
let mut archive = archive.namespace_append(&namespace);
let mut ranges = backup_object.ranges();
let range_count = ranges.len();
if range_count == 0 {
archive.put_empty(path).await;
} else if range_count == 1 {
let object = ranges.remove(0).object;
archive.put_object(&chunker, repo, path, object).await?;
} else {
let mut readers: Vec<(Extent, T)> = Vec::new();
for object in ranges {
let extent = Extent {
start: object.start,
end: object.end,
};
let object = object.object;
readers.push((extent, object));
}
archive
.put_sparse_object(&chunker, repo, path, readers)
.await?;
}
}
}
Ok(())
}
async fn store_object<B: BackendClone, C: AsyncChunker + Send + 'static>(
&self,
repo: &mut Repository<B>,
chunker: C,
archive: &ActiveArchive,
node: Node,
) -> Result<()> {
let objects = self.backup_object(node.clone()).await;
self.raw_store_object(repo, chunker, archive, node, objects)
.await
}
}
#[async_trait]
pub trait RestoreDriver<T: Write + Send + 'static>: RestoreTarget<T> {
async fn raw_retrieve_object<B: BackendClone>(
&self,
repo: &mut Repository<B>,
archive: &ActiveArchive,
node: Node,
objects: HashMap<String, RestoreObject<T>>,
) -> Result<()> {
let path = &node.path;
if node.is_file() {
for (namespace, restore_object) in objects {
let archive = archive.namespace_append(&namespace);
let mut ranges = restore_object.ranges();
let range_count = ranges.len();
if range_count == 1 {
let object = ranges.remove(0).object;
archive.get_object(repo, &path, object).await?;
} else {
let mut writers: Vec<(Extent, T)> = Vec::new();
for object in ranges {
let extent = Extent {
start: object.start,
end: object.end,
};
let object = object.object;
writers.push((extent, object));
}
archive.get_sparse_object(repo, &path, writers).await?;
}
}
}
Ok(())
}
async fn retrieve_object<B: BackendClone>(
&self,
repo: &mut Repository<B>,
archive: &ActiveArchive,
node: Node,
) -> Result<()> {
let objects = self.restore_object(node.clone()).await;
self.raw_retrieve_object(repo, archive, node, objects).await
}
}