camino_tempfile_ext/fixture/
errors.rs1use std::{error::Error, fmt};
7
8pub(crate) trait ChainError {
9 fn chain<F>(self, cause: F) -> Self
10 where
11 F: Error + Send + Sync + 'static;
12}
13
14pub(crate) trait ResultChainExt<T> {
15 fn chain<C>(self, chainable: C) -> Result<T, C>
16 where
17 C: ChainError;
18}
19
20impl<T, E> ResultChainExt<T> for Result<T, E>
21where
22 E: Error + Send + Sync + 'static,
23{
24 fn chain<C>(self, chainable: C) -> Result<T, C>
25 where
26 C: ChainError,
27 {
28 self.map_err(|e| chainable.chain(e))
29 }
30}
31
32#[derive(Copy, Clone, Debug, PartialEq, Eq)]
34#[non_exhaustive]
35pub enum FixtureKind {
36 Walk,
38 CopyFile,
40 WriteFile,
42 CreateDir,
44 Cleanup,
46 Symlink,
48}
49
50impl fmt::Display for FixtureKind {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match *self {
53 FixtureKind::Walk => write!(f, "error walking source tree"),
54 FixtureKind::CopyFile => write!(f, "error copying file"),
55 FixtureKind::WriteFile => write!(f, "error writing file"),
56 FixtureKind::CreateDir => write!(f, "error creating directory"),
57 FixtureKind::Cleanup => write!(f, "error cleaning up fixture"),
58 FixtureKind::Symlink => write!(f, "error creating symlink to target"),
59 }
60 }
61}
62
63#[derive(Debug)]
65pub struct FixtureError {
66 kind: FixtureKind,
67 source: Option<Box<dyn Error + Send + Sync + 'static>>,
68}
69
70impl FixtureError {
71 pub fn new(kind: FixtureKind) -> Self {
73 Self { kind, source: None }
74 }
75
76 pub fn with_source(
78 mut self,
79 source: impl Into<Box<dyn Error + Send + Sync + 'static>>,
80 ) -> Self {
81 self.source = Some(source.into());
82 self
83 }
84
85 pub fn kind(&self) -> FixtureKind {
87 self.kind
88 }
89}
90
91impl Error for FixtureError {
92 fn description(&self) -> &str {
93 "failed to initialize fixture"
94 }
95
96 fn source(&self) -> Option<&(dyn Error + 'static)> {
97 self.source.as_ref().map(|source| &**source as &dyn Error)
98 }
99}
100
101impl fmt::Display for FixtureError {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 write!(f, "failed to initialize fixture: {}", self.kind)
104 }
105}
106
107impl ChainError for FixtureError {
108 fn chain<F>(mut self, source: F) -> Self
109 where
110 F: Error + Send + Sync + 'static,
111 {
112 self.source = Some(Box::new(source));
113 self
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120 use anyhow::anyhow;
121 use std::io;
122
123 #[test]
124 fn error_types_work_with_source() {
125 let error = FixtureError::new(FixtureKind::WriteFile).with_source(io::Error::other("test"));
127 assert_eq!(error.kind(), FixtureKind::WriteFile);
128 assert_eq!(error.source().unwrap().to_string(), "test");
129
130 let error = FixtureError::new(FixtureKind::WriteFile).with_source(anyhow!("test"));
132 assert_eq!(error.kind(), FixtureKind::WriteFile);
133 assert_eq!(error.source().unwrap().to_string(), "test");
134
135 let error = FixtureError::new(FixtureKind::WriteFile)
137 .with_source(FixtureError::new(FixtureKind::CopyFile));
138 assert_eq!(error.kind(), FixtureKind::WriteFile);
139 assert_eq!(
140 error.source().unwrap().to_string(),
141 "failed to initialize fixture: error copying file"
142 );
143 }
144}