From 54086ef4c535babe18582ca8ad2a8f090311f4e3 Mon Sep 17 00:00:00 2001 From: Andreas Hartmann Date: Wed, 21 Jul 2021 09:17:57 +0200 Subject: [PATCH] backup_control: Implement custom error type Implements an error type based on `UError` that replaces the previously used error strings. The errors are currently returned when determining the backup mode, but extensions for future uses are already in place as comments. --- src/uucore/Cargo.toml | 1 + src/uucore/src/lib/mods/backup_control.rs | 142 ++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/src/uucore/Cargo.toml b/src/uucore/Cargo.toml index 6d27ecad4..e45c9e5b5 100644 --- a/src/uucore/Cargo.toml +++ b/src/uucore/Cargo.toml @@ -16,6 +16,7 @@ edition = "2018" path="src/lib/lib.rs" [dependencies] +clap = "2.33.3" dns-lookup = { version="1.0.5", optional=true } dunce = "1.0.0" getopts = "<= 0.2.21" diff --git a/src/uucore/src/lib/mods/backup_control.rs b/src/uucore/src/lib/mods/backup_control.rs index 5ef400813..a26cef073 100644 --- a/src/uucore/src/lib/mods/backup_control.rs +++ b/src/uucore/src/lib/mods/backup_control.rs @@ -1,5 +1,89 @@ +//! Implement GNU-style backup functionality. +//! +//! This module implements the backup functionality as described in the [GNU +//! manual][1]. It provides +//! +//! - pre-defined [`clap`-Arguments][2] for inclusion in utilities that +//! implement backups +//! - determination of the [backup mode][3] +//! - determination of the [backup suffix][4] +//! - [backup target path construction][5] +//! - [Error types][6] for backup-related errors +//! - GNU-compliant [help texts][7] for backup-related errors +//! +//! Backup-functionality is implemented by the following utilities: +//! +//! - `cp` +//! - `install` +//! - `ln` +//! - `mv` +//! +//! +//! [1]: https://www.gnu.org/software/coreutils/manual/html_node/Backup-options.html +//! [2]: arguments +//! [3]: `determine_backup_mode()` +//! [4]: `determine_backup_suffix()` +//! [5]: `get_backup_path()` +//! [6]: `BackupError` +//! [7]: `BACKUP_CONTROL_LONG_HELP` +//! +//! +//! # Usage example +//! +//! ``` +//! #[macro_use] +//! extern crate uucore; +//! +//! use clap::{App, Arg, ArgMatches}; +//! use std::path::{Path, PathBuf}; +//! use uucore::backup_control::{self, BackupMode}; +//! use uucore::error::{UError, UResult}; +//! +//! fn main() { +//! let usage = String::from("app [OPTION]... ARG"); +//! let long_usage = String::from("And here's a detailed explanation"); +//! +//! let matches = App::new("app") +//! .arg(backup_control::arguments::backup()) +//! .arg(backup_control::arguments::backup_no_args()) +//! .arg(backup_control::arguments::suffix()) +//! .usage(&usage[..]) +//! .after_help(&*format!( +//! "{}\n{}", +//! long_usage, +//! backup_control::BACKUP_CONTROL_LONG_HELP +//! )) +//! .get_matches_from(vec![ +//! "app", "--backup=t", "--suffix=bak~" +//! ]); +//! +//! let backup_mode = match backup_control::determine_backup_mode(&matches) { +//! Err(e) => { +//! show!(e); +//! return; +//! }, +//! Ok(mode) => mode, +//! }; +//! let backup_suffix = backup_control::determine_backup_suffix(&matches); +//! let target_path = Path::new("/tmp/example"); +//! +//! let backup_path = backup_control::get_backup_path( +//! backup_mode, target_path, &backup_suffix +//! ); +//! +//! // Perform your backups here. +//! +//! } +//! ``` + +// spell-checker:ignore backupopt + +use crate::error::{UError, UResult}; +use clap::ArgMatches; use std::{ env, + error::Error, + fmt::{Debug, Display}, path::{Path, PathBuf}, }; @@ -17,6 +101,12 @@ the VERSION_CONTROL environment variable. Here are the values: existing, nil numbered if numbered backups exist, simple otherwise simple, never always make simple backups"; +static VALID_ARGS_HELP: &str = "Valid arguments are: + - ‘none’, ‘off’ + - ‘simple’, ‘never’ + - ‘existing’, ‘nil’ + - ‘numbered’, ‘t’"; + #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum BackupMode { NoBackup, @@ -25,6 +115,58 @@ pub enum BackupMode { ExistingBackup, } +#[derive(Debug, Eq, PartialEq)] +pub enum BackupError { + InvalidArgument(String, String), + AmbiguousArgument(String, String), + BackupImpossible(), + // BackupFailed(PathBuf, PathBuf, std::io::Error), +} + +impl UError for BackupError { + fn code(&self) -> i32 { + match self { + BackupError::BackupImpossible() => 2, + _ => 1, + } + } + + fn usage(&self) -> bool { + // Suggested by clippy. + matches!( + self, + BackupError::InvalidArgument(_, _) | BackupError::AmbiguousArgument(_, _) + ) + } +} + +impl Error for BackupError {} + +impl Display for BackupError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use BackupError as BE; + match self { + BE::InvalidArgument(arg, origin) => write!( + f, + "invalid argument ‘{}’ for ‘{}’\n{}", + arg, origin, VALID_ARGS_HELP + ), + BE::AmbiguousArgument(arg, origin) => write!( + f, + "ambiguous argument ‘{}’ for ‘{}’\n{}", + arg, origin, VALID_ARGS_HELP + ), + BE::BackupImpossible() => write!(f, "cannot create backup"), + // Placeholder for later + // BE::BackupFailed(from, to, e) => Display::fmt( + // &uio_error!(e, "failed to backup '{}' to '{}'", from.display(), to.display()), + // f + // ), + } + } +} + +/// Arguments for backup-related functionality pub mod arguments { extern crate clap;