1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Error types for fixtures.

use std::error::Error;
use std::fmt;

pub(crate) trait ChainError {
    fn chain<F>(self, cause: F) -> Self
    where
        F: Error + Send + Sync + 'static;
}

pub(crate) trait ResultChainExt<T> {
    fn chain<C>(self, chainable: C) -> Result<T, C>
    where
        C: ChainError;

    fn chain_with<F, C>(self, chainable: F) -> Result<T, C>
    where
        F: FnOnce() -> C,
        C: ChainError;
}

impl<T, E> ResultChainExt<T> for Result<T, E>
where
    E: Error + Send + Sync + 'static,
{
    fn chain<C>(self, chainable: C) -> Result<T, C>
    where
        C: ChainError,
    {
        self.map_err(|e| chainable.chain(e))
    }

    fn chain_with<F, C>(self, chainable: F) -> Result<T, C>
    where
        F: FnOnce() -> C,
        C: ChainError,
    {
        self.map_err(|e| chainable().chain(e))
    }
}

/// Fixture initialization cause.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum FixtureKind {
    /// Failed when walking the source tree.
    Walk,
    /// Failed when copying a file.
    CopyFile,
    /// Failed when writing to a file.
    WriteFile,
    /// Failed when creating a directory.
    CreateDir,
    /// Failed to cleanup fixture.
    Cleanup,
    /// Failed to create symlink
    Symlink,
}

impl fmt::Display for FixtureKind {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            FixtureKind::Walk => write!(f, "Failed when walking the source tree,"),
            FixtureKind::CopyFile => write!(f, "Failed when copying a file."),
            FixtureKind::WriteFile => write!(f, "Failed when writing to a file."),
            FixtureKind::CreateDir => write!(f, "Failed when creating a directory."),
            FixtureKind::Cleanup => write!(f, "Failed to cleanup fixture."),
            FixtureKind::Symlink => write!(f, "Failed when symlinking to the target."),
        }
    }
}

/// Failure when initializing the fixture.
#[derive(Debug)]
pub struct FixtureError {
    kind: FixtureKind,
    cause: Option<Box<dyn Error + Send + Sync + 'static>>,
}

impl FixtureError {
    /// Create a `FixtureError`.
    pub fn new(kind: FixtureKind) -> Self {
        Self { kind, cause: None }
    }

    /// Fixture initialization cause.
    pub fn kind(&self) -> FixtureKind {
        self.kind
    }
}

impl Error for FixtureError {
    fn description(&self) -> &str {
        "Failed to initialize fixture"
    }

    fn cause(&self) -> Option<&dyn Error> {
        self.cause.as_ref().map(|c| {
            let c: &dyn Error = c.as_ref();
            c
        })
    }
}

impl fmt::Display for FixtureError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.cause {
            Some(ref cause) => write!(
                f,
                "Failed to initialize fixture: {}\nCause: {}",
                self.kind, cause
            ),
            None => write!(f, "Failed to initialize fixture: {}", self.kind),
        }
    }
}

impl ChainError for FixtureError {
    fn chain<F>(mut self, cause: F) -> Self
    where
        F: Error + Send + Sync + 'static,
    {
        self.cause = Some(Box::new(cause));
        self
    }
}