obnam-benchmark 0.1.0

a backup program
Documentation
/// A specification for a set of benchmarks for Obnam.
///
/// One specification can contain any number of benchmarks. For each
/// benchmark, any number of backups can be specified. For each
/// benchmark, the specification contains instructions for how to
/// create or change the data being backed up.
///
/// The specification can be serialized into linear sequence of steps
/// for execution.
use crate::step::Step;
use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::fs::File;
use std::path::{Path, PathBuf};

/// A benchmark specification.
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Specification {
    benchmarks: Vec<Benchmark>,
}

/// Possible errors from loading a specification from a file.
#[derive(Debug, thiserror::Error)]
pub enum SpecificationError {
    /// Two benchmarks have the same name.
    #[error("Duplicate benchmark name {0}")]
    DuplicateBenchmark(
        /// The name of the benchmark.
        String,
    ),

    /// I/O error opening the specification file.
    #[error("Couldn't open {0}: {1}")]
    Open(
        /// The name of the specification file.
        PathBuf,
        /// The I/O error.
        std::io::Error,
    ),

    /// YAML parsing problem in the specification file.
    #[error("Couldn't read YAML specification from {0}:\n  {1}")]
    Yaml(
        /// The name of the specification file.
        PathBuf,
        /// The YAML error.
        serde_yaml::Error,
    ),
}

#[derive(Debug, Serialize, Deserialize)]
struct Benchmark {
    benchmark: String,
    backups: Vec<Backup>,
}

#[derive(Debug, Serialize, Deserialize)]
struct Backup {
    pub changes: Vec<Change>,
}

#[derive(Debug, Serialize, Deserialize)]
pub(crate) enum Change {
    #[serde(rename = "create")]
    Create(Create),

    #[serde(rename = "delete")]
    Delete(FileCount),

    #[serde(rename = "rename")]
    Rename(FileCount),
}

/// How many files to create, and how big they should be.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Create {
    /// File count.
    pub files: u64,
    /// Size, in bytes, of each file.
    pub file_size: u64,
}

/// How many files to operate on.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileCount {
    /// File count.
    pub files: u64,
}

impl Specification {
    /// Load a benchmark specification from a named file.
    pub fn from_file(filename: &Path) -> Result<Self, SpecificationError> {
        let f = File::open(filename)
            .map_err(|err| SpecificationError::Open(filename.to_path_buf(), err))?;
        let spec: Specification = serde_yaml::from_reader(f)
            .map_err(|err| SpecificationError::Yaml(filename.to_path_buf(), err))?;
        spec.check()?;
        Ok(spec)
    }

    fn check(&self) -> Result<(), SpecificationError> {
        let mut names = HashSet::new();
        for name in self.benchmarks.iter().map(|b| b.benchmark.to_string()) {
            if names.contains(&name) {
                return Err(SpecificationError::DuplicateBenchmark(name));
            }
            names.insert(name);
        }
        Ok(())
    }

    /// Serialize the specification into a sequence of steps to execute it.
    pub fn steps(&self) -> Vec<Step> {
        let mut steps = vec![];
        for b in self.benchmarks.iter() {
            let n = b.backups.len();
            let after_base = n;
            let restore_base = 2 * n;

            steps.push(Step::Start(b.benchmark.to_string()));
            for (i, backup) in b.backups.iter().enumerate() {
                for change in backup.changes.iter() {
                    steps.push(Step::from(change));
                }
                steps.push(Step::ManifestLive(i));
                steps.push(Step::Backup(i));
                let after = after_base + i;
                steps.push(Step::ManifestLive(after));
                steps.push(Step::CompareManifests(i, after));
            }
            for (i, _) in b.backups.iter().enumerate() {
                steps.push(Step::Restore(i));
                let restored = restore_base + i;
                steps.push(Step::ManifestRestored(restored));
                steps.push(Step::CompareManifests(i, restored));
            }
            steps.push(Step::Stop(b.benchmark.to_string()));
        }
        steps
    }
}