use std::path::Path;
use chrono::{DateTime, Local};
use serde::Serialize;
use crate::applebooks::macos::utils::APPLEBOOKS_VERSION;
use crate::applebooks::macos::ABDatabase;
use crate::result::Result;
use crate::strings;
pub const DIRECTORY_TEMPLATE: &str = "{{ now | date(format='%Y-%m-%d-%H%M%S')}}-{{ version }}";
#[derive(Debug, Clone, Copy)]
pub struct BackupRunner;
impl BackupRunner {
pub fn run<O>(databases: &Path, output: &Path, options: O) -> Result<()>
where
O: Into<BackupOptions>,
{
let options: BackupOptions = options.into();
Self::backup(databases, output, options)?;
Ok(())
}
pub fn backup(databases: &Path, output: &Path, options: BackupOptions) -> Result<()> {
let directory_template = if let Some(template) = options.directory_template {
Self::validate_template(&template)?;
template
} else {
DIRECTORY_TEMPLATE.to_string()
};
let directory_name = Self::render_directory_name(&directory_template)?;
let destination_root = output.join(directory_name);
for name in &[
ABDatabase::Books.to_string(),
ABDatabase::Annotations.to_string(),
] {
let source = databases.join(name.clone());
let destination = destination_root.join(name);
crate::utils::copy_dir(source, destination)?;
}
Ok(())
}
fn validate_template(template: &str) -> Result<()> {
Self::render_directory_name(template).map(|_| ())
}
fn render_directory_name(template: &str) -> Result<String> {
let context = BackupNameContext::default();
strings::render_and_sanitize(template, context)
}
}
#[derive(Debug)]
pub struct BackupOptions {
pub directory_template: Option<String>,
}
#[derive(Debug, Serialize)]
struct BackupNameContext {
now: DateTime<Local>,
version: String,
}
impl Default for BackupNameContext {
fn default() -> Self {
Self {
now: Local::now(),
version: APPLEBOOKS_VERSION.to_owned(),
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::defaults::test::TemplatesDirectory;
use crate::utils;
#[test]
fn default_directory_template() {
let context = BackupNameContext::default();
strings::render_and_sanitize(DIRECTORY_TEMPLATE, context).unwrap();
}
#[test]
fn valid_context() {
let template =
utils::testing::load_template_str(TemplatesDirectory::ValidContext, "valid-backup.txt");
let context = BackupNameContext::default();
strings::render_and_sanitize(&template, context).unwrap();
}
#[test]
#[should_panic(expected = "Failed to render '__tera_one_off'")]
fn invalid_context() {
let template = utils::testing::load_template_str(
TemplatesDirectory::InvalidContext,
"invalid-backup.txt",
);
let context = BackupNameContext::default();
strings::render_and_sanitize(&template, context).unwrap();
}
}