1
use crate::chunker::AsyncChunker;
2
use crate::manifest::archive::{ActiveArchive, Extent};
3
use crate::manifest::target::{BackupObject, BackupTarget, RestoreObject, RestoreTarget};
4
use crate::repository::{BackendClone, Repository};
5

6
use asuran_core::manifest::listing::Node;
7

8
use async_trait::async_trait;
9
use thiserror::Error;
10

11
use std::collections::HashMap;
12
use std::io::{Read, Write};
13

14
/// An error for things that can go wrong with drivers
15
#[derive(Error, Debug)]
16
#[non_exhaustive]
17
pub enum DriverError {
18
    #[error("")]
19
    ArchiveError(#[from] crate::manifest::archive::ArchiveError),
20
}
21

22
type Result<T> = std::result::Result<T, DriverError>;
23

24
/// Defines a type that can, semi-automatically, drive the storage of objects from
25
/// an associated `BackupTarget` into a repository.
26
///
27
/// As this is effectively an extension trait for a `BackupTarget`, and the behavior
28
/// will usually be more or less the same, reasonable default implementations have
29
/// been provided.
30
#[async_trait]
31
pub trait BackupDriver<T: Read + Send + 'static>: BackupTarget<T> {
32
    /// Inserts an object into the repository using the output from
33
    /// `BackupTarget::backup_object`
34
    ///
35
    /// This method should only be used directly when you want to modify the data in
36
    /// route, otherwise use `store_object`.
37
    ///
38
    /// Stores objects in sub-namespaces of the namespace of the archive object provided
39 1
    async fn raw_store_object<B: BackendClone, C: AsyncChunker + Send + 'static>(
40
        &self,
41
        repo: &mut Repository<B>,
42
        chunker: C,
43
        archive: &ActiveArchive,
44
        node: Node,
45
        objects: HashMap<String, BackupObject<T>>,
46
        ex: &smol::Executor<'_>,
47
    ) -> Result<()> {
48 1
        if node.is_file() {
49 1
            for (namespace, backup_object) in objects {
50 1
                let path = &node.path;
51
                // TODO (#45): Store total size in archive
52
                // let total_size = backup_object.total_size();
53
                // Get a new archive with the specified namespace
54 1
                let mut archive = archive.namespace_append(&namespace);
55
                // Pull ranges out of object and determine sparsity
56 1
                let mut ranges = backup_object.ranges();
57
                // Determine sparsity and load object into repository
58 1
                let range_count = ranges.len();
59 1
                if range_count == 0 {
60 1
                    archive.put_empty(path).await;
61 1
                } else if range_count == 1 {
62 1
                    let object = ranges.remove(0).object;
63 1
                    archive.put_object(&chunker, repo, path, object, ex).await?;
64
                } else {
65 0
                    let mut readers: Vec<(Extent, T)> = Vec::new();
66 0
                    for object in ranges {
67 0
                        let extent = Extent {
68 0
                            start: object.start,
69 0
                            end: object.end,
70
                        };
71 0
                        let object = object.object;
72 0
                        readers.push((extent, object));
73
                    }
74 0
                    archive
75 0
                        .put_sparse_object(&chunker, repo, path, readers, ex)
76 0
                        .await?;
77
                }
78
            }
79
        }
80 1
        Ok(())
81
    }
82

83
    /// Convenience method that performs a call to `self.backup_object` for you and
84
    /// routes the results into `self.raw_store_object`
85 1
    async fn store_object<B: BackendClone, C: AsyncChunker + Send + 'static>(
86
        &self,
87
        repo: &mut Repository<B>,
88
        chunker: C,
89
        archive: &ActiveArchive,
90
        node: Node,
91
        ex: &smol::Executor<'_>,
92
    ) -> Result<()> {
93 1
        let objects = self.backup_object(node.clone()).await;
94 1
        self.raw_store_object(repo, chunker, archive, node, objects, ex)
95 0
            .await
96
    }
97
}
98

99
/// Defines a type that can, semi-automatically, drive the retrieval of objects from
100
/// a repository into an associated `RestoreTarget`.
101
///
102
/// As this is effectively an extension trait for a `RestoreTarget`, and the
103
/// behavior will usually be more or less the same, reasonable default
104
/// implementations have been provided.
105
#[async_trait]
106
pub trait RestoreDriver<T: Write + Send + 'static>: RestoreTarget<T> {
107
    /// Retrives an object from the repository using the output from RestoreTarget::restore_object
108
    ///
109
    /// This method should really only be used directly when you want to change the data in route,
110
    /// otherwise use retrive_object.
111
    ///
112
    /// Retrives objects from the stub-namespaces of the namespace of the object provided
113 1
    async fn raw_retrieve_object<B: BackendClone>(
114
        &self,
115
        repo: &mut Repository<B>,
116
        archive: &ActiveArchive,
117
        node: Node,
118
        objects: HashMap<String, RestoreObject<T>>,
119
    ) -> Result<()> {
120 1
        let path = &node.path;
121 1
        if node.is_file() {
122 1
            for (namespace, restore_object) in objects {
123
                // TODO (#45): get total size and do something with it
124
                // Get a new archive with the specified namespace
125 1
                let archive = archive.namespace_append(&namespace);
126
                // Pull ranges out of object and determine sparsity
127 1
                let mut ranges = restore_object.ranges();
128
                // determin sparsity and retrieve object from repository
129 1
                let range_count = ranges.len();
130
                // This does not have a case for zero, as the target method should have already created
131
                // an empty object
132 1
                if range_count == 1 {
133 1
                    let object = ranges.remove(0).object;
134 1
                    archive.get_object(repo, &path, object).await?;
135
                // This used to be a if range count > 1, this may cause issues
136
                } else {
137 0
                    let mut writers: Vec<(Extent, T)> = Vec::new();
138 0
                    for object in ranges {
139 0
                        let extent = Extent {
140 0
                            start: object.start,
141 0
                            end: object.end,
142
                        };
143 0
                        let object = object.object;
144 0
                        writers.push((extent, object));
145
                    }
146 0
                    archive.get_sparse_object(repo, &path, writers).await?;
147
                }
148
            }
149
        }
150 1
        Ok(())
151
    }
152

153
    /// Retrieves an object, performing the call to BackupTarget::restore_object and raw_retrive_object
154
    /// for you.
155 1
    async fn retrieve_object<B: BackendClone>(
156
        &self,
157
        repo: &mut Repository<B>,
158
        archive: &ActiveArchive,
159
        node: Node,
160
    ) -> Result<()> {
161 1
        let objects = self.restore_object(node.clone()).await;
162 1
        self.raw_retrieve_object(repo, archive, node, objects).await
163
    }
164
}

Read our documentation on viewing source code .

Loading