1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//! This module provides the root object of the object graph of a repository.
//!
//! The manifest contains a list of all the archives in the repository, as well
//! default chunk settings, and a time stamp for preventing replay attacks.
//!
//! All operations on a manifest require a reference to the repository for context.
//! The repository is not encapsulated in the manifest because the manifest needs
//! to be triviallly serializeable and deserilazeable.
pub mod archive;
pub mod driver;
pub mod target;

use crate::repository::backend::Manifest as BackendManifest;
use crate::repository::backend::Result;
use crate::repository::{Backend, BackendClone, ChunkSettings, Repository};

use chrono::prelude::*;
use serde::{Deserialize, Serialize};

pub use self::archive::{ActiveArchive, StoredArchive};

/// Repository manifest
///
/// This is the root object of the repository, all objects that are active can
/// be reached through the Mainfest.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Manifest<T: Backend> {
    internal_manifest: T::Manifest,
}

impl<T: BackendClone> Manifest<T> {
    /// Loads the manifest from the repository
    ///
    /// # Panics
    ///
    /// Will panic if loading the manifest fails
    pub fn load(repo: &Repository<T>) -> Manifest<T>
    where
        T: Backend,
    {
        let internal_manifest = repo.backend_manifest();
        Manifest { internal_manifest }
    }

    /// Set the Chunk Settings used by the repository
    pub async fn set_chunk_settings(&mut self, settings: ChunkSettings) -> Result<()> {
        self.internal_manifest.write_chunk_settings(settings).await
    }

    /// Gets the default Chunk Settings for the repository
    pub async fn chunk_settings(&mut self) -> ChunkSettings {
        self.internal_manifest.chunk_settings().await
    }

    /// Commits an archive to the manifest, then the manifest to the repository
    ///
    /// Consumes the repository while commiting it.
    ///
    /// # Panics
    ///
    /// Will panic if commiting the archive to the repository fails
    pub async fn commit_archive(
        &mut self,
        repo: &mut Repository<impl BackendClone>,
        archive: ActiveArchive,
    ) -> Result<()> {
        let stored_archive = archive.store(repo).await;
        self.internal_manifest.write_archive(stored_archive).await?;
        repo.commit_index().await;
        Ok(())
    }

    /// Returns a copy of the list of archives in this repository
    ///
    /// Theses can be converted into full archives with `StoredArchive::load`
    pub async fn archives(&mut self) -> Vec<StoredArchive> {
        self.internal_manifest.archive_iterator().await.collect()
    }

    /// Provides the timestamp of the manifest's last modification
    pub async fn timestamp(&mut self) -> Result<DateTime<FixedOffset>> {
        self.internal_manifest.last_modification().await
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::repository::*;

    #[tokio::test(threaded_scheduler)]
    async fn chunk_settings_sanity() {
        let settings = ChunkSettings {
            encryption: Encryption::NoEncryption,
            compression: Compression::NoCompression,
            hmac: HMAC::Blake2b,
        };

        let key = Key::random(32);
        let backend = crate::repository::backend::mem::Mem::new(settings, key.clone());
        let repo = Repository::with(backend, settings, key);
        let mut manifest = Manifest::load(&repo);

        manifest.set_chunk_settings(settings).await.unwrap();
        let new_settings = manifest.chunk_settings().await;

        assert_eq!(settings, new_settings);
    }

    #[tokio::test(threaded_scheduler)]
    async fn new_archive_updates_time() {
        let settings = ChunkSettings::lightweight();
        let key = Key::random(32);
        let backend = crate::repository::backend::mem::Mem::new(settings, key.clone());
        let repo = Repository::with(backend.clone(), settings, key);

        let mut manifest = Manifest::load(&repo);

        let dummy1 = StoredArchive::dummy_archive();
        backend.get_manifest().write_archive(dummy1).await.unwrap();
        let time1 = manifest.timestamp().await.unwrap();
        std::thread::sleep(std::time::Duration::from_millis(10));
        let dummy2 = StoredArchive::dummy_archive();
        backend.get_manifest().write_archive(dummy2).await.unwrap();
        let time2 = manifest.timestamp().await.unwrap();

        assert!(time2 > time1);
    }
}