~iptq/garbage

[PATCH 2/3] Adds context to lots of errors.

Details
Message ID
<8YQd3dnz8JEvt1mDpZ2d71Tolu6IxIlmynuXprmPmQ@cp3-web-021.plabs.ch>
DKIM signature
missing
Download raw message
Patch: +129 -35
---
 src/dir.rs         | 37 ++++++++++++++++++++++------------
 src/errors.rs      | 37 ++++++++++++++++++++++++++++++++--
 src/ops/empty.rs   | 30 +++++++++++++++++++++++-----
 src/ops/list.rs    |  5 +++--
 src/ops/put.rs     | 50 ++++++++++++++++++++++++++++++++++++----------
 src/ops/restore.rs |  5 +++--
 6 files changed, 129 insertions(+), 35 deletions(-)

diff --git a/src/dir.rs b/src/dir.rs
index b4efc1f..d06cb07 100644
--- a/src/dir.rs
+++ b/src/dir.rs
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};

use walkdir::{DirEntry, WalkDir};

use crate::Error;
use crate::errors::{Context, Error};
use crate::TrashInfo;
use crate::XDG;

@@ -44,7 +44,9 @@ impl TrashDir {
    pub fn create(&self) -> Result<(), Error> {
        let path = &self.0;
        if !path.exists() {
            fs::create_dir_all(&path)?;
            fs::create_dir_all(&path).with_context(|| {
                format!("Failed to create {}", path.display())
            })?;
        }
        Ok(())
    }
@@ -58,7 +60,9 @@ impl TrashDir {
    pub fn files_dir(&self) -> Result<PathBuf, Error> {
        let target = self.0.join("files");
        if !target.exists() {
            fs::create_dir_all(&target)?;
            fs::create_dir_all(&target).with_context(|| {
                format!("Failed to create {}", target.display())
            })?;
        }
        Ok(target)
    }
@@ -67,7 +71,9 @@ impl TrashDir {
    pub fn info_dir(&self) -> Result<PathBuf, Error> {
        let target = self.0.join("info");
        if !target.exists() {
            fs::create_dir_all(&target)?;
            fs::create_dir_all(&target).with_context(|| {
                format!("Failed to create {}", target.display())
            })?;
        }
        Ok(target)
    }
@@ -84,13 +90,15 @@ impl TrashDir {

    /// Iterate over trash infos within this trash directory
    pub fn iter(&self) -> Result<TrashDirIter, Error> {
        let iter = WalkDir::new(&self.info_dir()?)
            .contents_first(true)
            .into_iter()
            .filter_entry(|entry| match entry.path().extension() {
                Some(x) => x == "trashinfo",
                _ => false,
            });
        let iter = WalkDir::new(
            &self.info_dir().context("Failed to open trash directory")?,
        )
        .contents_first(true)
        .into_iter()
        .filter_entry(|entry| match entry.path().extension() {
            Some(x) => x == "trashinfo",
            _ => false,
        });
        Ok(TrashDirIter(self.0.clone(), Box::new(iter)))
    }
}
@@ -133,8 +141,11 @@ impl Iterator for TrashDirIter {
        };

        Some(
            TrashInfo::from_files(entry.path(), deleted_path)
                .map_err(Error::from),
            TrashInfo::from_files(entry.path(), &deleted_path)
                .map_err(Error::from)
                .with_context(|| {
                    format!("Failed to load trashinfo file {} (for deleted file {})", entry.path().display(), deleted_path.display())
                }),
        )
    }
}
diff --git a/src/errors.rs b/src/errors.rs
index 9db81ae..93e5525 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -6,6 +6,30 @@ use std::{
/// Result convenience type for the Error type
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// An extension trait to add the `with_context` method.
pub trait Context: Sized {
    type Output;

    /// Adds an entry of context to the error.
    fn context(self, msg: &str) -> Self::Output {
        self.with_context(|| msg.to_string())
    }

    /// Adds an entry of context computed by a function to the error.
    fn with_context<F: FnOnce() -> String>(self, msg: F) -> Self::Output;
}

impl<T, E> Context for Result<T, E>
where
    Error: From<E>,
{
    type Output = Result<T, Error>;

    fn with_context<F: FnOnce() -> String>(self, msg: F) -> Self::Output {
        self.map_err(|e| Error::from(e).with_context(msg))
    }
}

/// Errors plus their surrounding context.
#[derive(Debug, Error)]
pub struct Error {
@@ -19,9 +43,9 @@ pub struct Error {

impl Display for Error {
    fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
        writeln!(fmt, "{}", self.kind)?;
        write!(fmt, "{}", self.kind)?;
        for entry in self.context.iter().rev() {
            writeln!(fmt, "caused by {}", entry)?;
            write!(fmt, "\ncaused by: {}", entry)?;
        }
        Ok(())
    }
@@ -36,6 +60,15 @@ impl<T: Into<ErrorKind>> From<T> for Error {
    }
}

impl Context for Error {
    type Output = Self;

    fn with_context<F: FnOnce() -> String>(mut self, msg: F) -> Self {
        self.context.push(msg());
        self
    }
}

/// All errors that could happen
#[derive(Debug, Error)]
#[allow(missing_docs)]
diff --git a/src/ops/empty.rs b/src/ops/empty.rs
index aca485b..cc95fd7 100644
--- a/src/ops/empty.rs
+++ b/src/ops/empty.rs
@@ -4,7 +4,7 @@ use std::path::PathBuf;

use chrono::{Duration, Local};

use crate::errors::Result;
use crate::errors::{Context, Result};
use crate::TrashDir;

/// Options to pass to empty
@@ -42,7 +42,8 @@ pub fn empty(options: EmptyOptions) -> Result<()> {
        Local::now()
    };

    let current_dir = env::current_dir()?;
    let current_dir =
        env::current_dir().context("Failed to get current directory")?;
    trash_dir
        .iter()?
        .collect::<Result<Vec<_>>>()?
@@ -54,13 +55,32 @@ pub fn empty(options: EmptyOptions) -> Result<()> {
            if options.dry {
                println!("deleting {:?}", info.path);
            } else {
                fs::remove_file(info.info_path)?;
                fs::remove_file(&info.info_path).with_context(|| {
                    format!(
                        "Failed to delete info file {}",
                        info.info_path.display()
                    )
                })?;

                if info.deleted_path.exists() {
                    if info.deleted_path.is_dir() {
                        fs::remove_dir_all(info.deleted_path)?;
                        fs::remove_dir_all(&info.deleted_path).with_context(
                            || {
                                format!(
                                    "Failed to delete trashed directory {}",
                                    info.deleted_path.display()
                                )
                            },
                        )?;
                    } else {
                        fs::remove_file(info.deleted_path)?;
                        fs::remove_file(&info.deleted_path).with_context(
                            || {
                                format!(
                                    "Failed to delete trashed file {}",
                                    info.deleted_path.display()
                                )
                            },
                        )?;
                    }
                }
            }
diff --git a/src/ops/list.rs b/src/ops/list.rs
index 70627ec..a501fff 100644
--- a/src/ops/list.rs
+++ b/src/ops/list.rs
@@ -2,7 +2,7 @@ use std::env;
use std::path::PathBuf;

use crate::dir::TrashDir;
use crate::errors::Result;
use crate::errors::{Context, Result};
use crate::list;

/// Options to pass to list
@@ -23,7 +23,8 @@ pub struct ListOptions {
pub fn list(options: ListOptions) -> Result<()> {
    let trash_dir = TrashDir::from_opt(options.trash_dir.as_ref());

    let current_dir = env::current_dir()?;
    let current_dir =
        env::current_dir().context("Failed to get current directory")?;

    let mut files = trash_dir
        .iter()?
diff --git a/src/ops/put.rs b/src/ops/put.rs
index 50bc2e6..b18f395 100644
--- a/src/ops/put.rs
+++ b/src/ops/put.rs
@@ -6,8 +6,8 @@ use std::path::{Path, PathBuf};

use chrono::Local;

use crate::errors::{ErrorKind, Result};
use crate::utils::{self};
use crate::errors::{Context, ErrorKind, Result};
use crate::utils;
use crate::{TrashDir, TrashInfo};
use crate::{HOME_MOUNT, MOUNTS};

@@ -66,10 +66,13 @@ pub struct PutOptions {
/// Throw some files into the trash.
pub fn put(options: PutOptions) -> Result<()> {
    for path in options.paths.iter() {
        let abs_path = utils::into_absolute(&path)?;
        let abs_path = utils::into_absolute(&path).with_context(|| {
            format!("Couldn't convert {} to an absolute path", path.display())
        })?;

        // don't allow deleting '.' or '..'
        let current_dir = env::current_dir()?;
        let current_dir =
            env::current_dir().context("Couldn't get current directory")?;
        let parent = current_dir.parent();
        info!("Checking if {:?} is . or ..", abs_path);
        trace!("curr = {:?}", current_dir);
@@ -134,7 +137,9 @@ impl DeletionStrategy {
                .join(".Trash")
                .join(utils::get_uid().to_string());
            let trash_dir = TrashDir::from(topdir_trash_dir);
            trash_dir.create()?;
            trash_dir
                .create()
                .context("Failed to create trash directory")?;
            return Ok(DeletionStrategy::MoveTo(trash_dir));
        }

@@ -143,7 +148,9 @@ impl DeletionStrategy {
            let topdir_trash_uid =
                target_mount.join(format!(".Trash-{}", utils::get_uid()));
            let trash_dir = TrashDir::from(topdir_trash_uid);
            trash_dir.create()?;
            trash_dir
                .create()
                .context("Failed to create trash directory")?;
            return Ok(DeletionStrategy::MoveTo(trash_dir));
        }

@@ -187,12 +194,14 @@ impl DeletionStrategy {
                eprint!("Remove file '{}'? [Y/n] ", target.to_str().unwrap());
            }
            // TODO: actually handle prompting instead of manually flushing
            io::stderr().flush()?;
            io::stderr().flush().context("Failed to flush stderr")?;

            let should_continue = loop {
                let stdin = io::stdin();
                let mut s = String::new();
                stdin.read_line(&mut s).unwrap();
                stdin
                    .read_line(&mut s)
                    .context("Failed to read from stdin")?;
                match s.trim().to_lowercase().as_str() {
                    "yes" | "y" => break true,
                    "no" | "n" => break false,
@@ -246,10 +255,29 @@ impl DeletionStrategy {

        // copy the file over
        if requires_copy {
            utils::recursive_copy(&target, &trash_file_path)?;
            fs::remove_dir_all(&target)?;
            utils::recursive_copy(&target, &trash_file_path).with_context(
                || {
                    format!(
                        "Failed to copy file {} to trash ({})",
                        target.display(),
                        trash_file_path.display()
                    )
                },
            )?;
            fs::remove_dir_all(&target).with_context(|| {
                format!(
                    "Failed to delete original file {} after copying",
                    target.display()
                )
            })?;
        } else {
            fs::rename(&target, &trash_file_path)?;
            fs::rename(&target, &trash_file_path).with_context(|| {
                format!(
                    "Failed to move file {} to trash ({})",
                    target.display(),
                    trash_file_path.display()
                )
            })?;
        }

        Ok(())
diff --git a/src/ops/restore.rs b/src/ops/restore.rs
index c0d363e..4a179fe 100644
--- a/src/ops/restore.rs
+++ b/src/ops/restore.rs
@@ -3,7 +3,7 @@ use std::fs;
use std::io;
use std::path::PathBuf;

use crate::errors::{ErrorKind, Result};
use crate::errors::{Context, ErrorKind, Result};
use crate::list;
use crate::TrashDir;

@@ -34,7 +34,8 @@ pub fn restore(options: RestoreOptions) -> Result<()> {

    // get list of files sorted by deletion date
    // TODO: possible to get this to be streaming?
    let current_dir = env::current_dir()?;
    let current_dir =
        env::current_dir().context("Couldn't get current directory")?;
    let files = {
        let mut files = trash_dir
            .iter()?
-- 
2.28.0
Reply to thread Export thread (mbox)