forge_backup 1.2.0

A program to backup all the user home folders to an S3 bucket.
Documentation
use std::{fmt, path::PathBuf};

pub type AppResult<T> = Result<T, AppError>;

#[derive(Debug)]
pub enum AppError {
    Backup(BackupError),
    Config(ConfigError),
    Mail(MailerError),
}

#[derive(Debug)]
pub enum ConfigError {
    FileNotFoundError(PathBuf),
    DeserializationError(String, toml::de::Error),
    ProjectPathNotFoundError,
    EnvVarSubstitutionError(String),
}

#[derive(Debug)]
pub enum MailerError {
    SendError,
    ConfigError(String),
}

#[derive(Debug)]
pub enum BackupError {
    DirectoryReadError(PathBuf),
    MakeDirectoryError(PathBuf),
    MissingTempError(PathBuf),
    MissingHomeError(PathBuf),
    InvalidHomeError(PathBuf),
    CreateArchiveError(CreateArchiveError),
    S3CopyError(S3CopyError),
    S3InvalidFile(PathBuf),
    ZipError(String),
    DeleteTempError(PathBuf),
}

#[derive(Debug)]
pub struct CreateArchiveError {
    pub path: PathBuf,
    pub user: String,
}

#[derive(Debug)]
pub struct S3CopyError {
    pub src: String,
    pub dest: String,
    pub std_err: String,
    pub std_out: String,
}

impl fmt::Display for ConfigError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ConfigError::FileNotFoundError(path) => {
                write!(f, "Configuration file not found. {path:?}")
            }
            ConfigError::DeserializationError(_, err) => {
                write!(f, "Failed to deserialize the configuration file: {err}")
            }
            ConfigError::ProjectPathNotFoundError => {
                write!(f, "Could not determine project directories.")
            }
            ConfigError::EnvVarSubstitutionError(var) => {
                write!(f, "Could not find environment variable: {var}")
            }
        }
    }
}
impl fmt::Display for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            AppError::Config(ce) => ce.fmt(f),
            AppError::Backup(be) => be.fmt(f),
            AppError::Mail(me) => me.fmt(f),
        }
    }
}
impl fmt::Display for MailerError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Unable to send email")
    }
}
impl fmt::Display for BackupError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            BackupError::ZipError(s) => {
                write!(f, "Unable to run zip to compress the archive: {s}")
            }
            BackupError::DirectoryReadError(path) => {
                write!(
                    f,
                    "Unable to read directory during backup: {}",
                    path.display()
                )
            }
            BackupError::MakeDirectoryError(path) => {
                write!(
                    f,
                    "Unable to create directory during backup: {}",
                    path.display()
                )
            }
            BackupError::MissingHomeError(path) => {
                write!(f, "Unable to find home folder: {}", path.display())
            }
            BackupError::MissingTempError(path) => {
                write!(f, "Missing temp directory: {}", path.display())
            }
            BackupError::DeleteTempError(path) => {
                write!(f, "Unable to delete temp file: {}", path.display())
            }
            BackupError::InvalidHomeError(path) => {
                write!(f, "Invalid home directory: {}", path.display())
            }
            BackupError::CreateArchiveError(create_archive_error) => {
                write!(
                    f,
                    "Unable to create archive at: {} for user: {}",
                    create_archive_error.path.display(),
                    create_archive_error.user
                )
            }
            BackupError::S3InvalidFile(file) => {
                write!(f, "Invalid source file: {}", file.display())
            }
            BackupError::S3CopyError(s3_copy_error) => {
                write!(
                    f,
                    "Unable to copy from: {} to {}: {}",
                    s3_copy_error.src, s3_copy_error.dest, s3_copy_error.std_err
                )
            }
        }
    }
}

impl std::error::Error for ConfigError {}

impl From<ConfigError> for AppError {
    fn from(error: ConfigError) -> Self {
        AppError::Config(error)
    }
}

impl From<MailerError> for AppError {
    fn from(error: MailerError) -> Self {
        AppError::Mail(error)
    }
}

impl From<BackupError> for AppError {
    fn from(error: BackupError) -> Self {
        AppError::Backup(error)
    }
}