Skip to main content

studiole_report/attachments/
attach.rs

1//! Trait for attaching additional context to errors and results.
2use crate::prelude::*;
3
4/// Attach additional context to an error or a result containing one.
5pub trait Attach: Sized {
6    /// Attach a name and value.
7    #[must_use]
8    fn attach(self, name: impl Into<String>, value: impl Display) -> Self;
9
10    /// Attach a name and lazily evaluated value.
11    #[must_use]
12    fn attach_with<D: Display>(self, name: impl Into<String>, value: impl FnOnce() -> D) -> Self;
13
14    /// Attach a [`Path`].
15    #[must_use]
16    fn attach_path(self, path: impl AsRef<Path>) -> Self;
17}
18
19impl Attach for StructuredError {
20    fn attach(mut self, name: impl Into<String>, value: impl Display) -> Self {
21        self.attached.push(Attachment::new(name, value));
22        self
23    }
24    fn attach_with<D: Display>(
25        mut self,
26        name: impl Into<String>,
27        value: impl FnOnce() -> D,
28    ) -> Self {
29        self.attached.push(Attachment::with(name, value));
30        self
31    }
32    fn attach_path(mut self, path: impl AsRef<Path>) -> Self {
33        self.attached.push(Attachment::path(path));
34        self
35    }
36}
37
38impl<T: StdError + Send + Sync + 'static> Attach for Report<T> {
39    fn attach(mut self, name: impl Into<String>, value: impl Display) -> Self {
40        self.inner.attached.push(Attachment::new(name, value));
41        self
42    }
43    fn attach_with<D: Display>(
44        mut self,
45        name: impl Into<String>,
46        value: impl FnOnce() -> D,
47    ) -> Self {
48        self.inner.attached.push(Attachment::with(name, value));
49        self
50    }
51    fn attach_path(mut self, path: impl AsRef<Path>) -> Self {
52        self.inner.attached.push(Attachment::path(path));
53        self
54    }
55}
56
57impl<T, A: Attach> Attach for Result<T, A> {
58    fn attach(self, name: impl Into<String>, value: impl Display) -> Self {
59        self.map_err(|e| e.attach(name, value))
60    }
61    fn attach_with<D: Display>(self, name: impl Into<String>, value: impl FnOnce() -> D) -> Self {
62        self.map_err(|e| e.attach_with(name, value))
63    }
64    fn attach_path(self, path: impl AsRef<Path>) -> Self {
65        self.map_err(|e| e.attach_path(path))
66    }
67}
68
69#[cfg(test)]
70mod tests {
71    use super::*;
72
73    #[test]
74    fn structured_error_attach() {
75        // Arrange
76        let error = StructuredError::new(OuterError::Operation);
77        // Act
78        let error = error.attach("key", "value");
79        // Assert
80        assert_eq!(error.attached.len(), 1);
81    }
82
83    #[test]
84    fn report_attach() {
85        // Arrange
86        let report = Report::new(OuterError::Operation);
87        // Act
88        let error = report.attach("key", "value");
89        // Assert
90        assert_eq!(error.attached.len(), 1);
91    }
92
93    #[test]
94    fn result_attach__on_err() {
95        // Arrange
96        let result: Result<i32, Report<OuterError>> = Err(Report::new(OuterError::Operation));
97        // Act
98        let report = result.attach("key", "value");
99        // Assert
100        assert_eq!(report.expect_err("should be err").attached.len(), 1);
101    }
102
103    #[test]
104    fn result_attach__on_ok() {
105        // Arrange
106        let result: Result<i32, Report<OuterError>> = Ok(42);
107        // Act
108        let result = result.attach("key", "value");
109        // Assert
110        assert_eq!(result.expect("should be ok"), 42);
111    }
112
113    #[test]
114    fn result_attach__attach_path() {
115        // Arrange
116        let result: Result<i32, Report<OuterError>> = Err(Report::new(OuterError::Operation));
117        // Act
118        let result = result.attach_path("/tmp/data.bin");
119        // Assert
120        assert_eq!(result.expect_err("should be err").attached.len(), 1);
121    }
122
123    #[test]
124    fn result_attach__attach_with() {
125        // Arrange
126        let result: Result<i32, Report<OuterError>> = Err(Report::new(OuterError::Operation));
127        // Act
128        let result = result.attach_with("count", || 7);
129        // Assert
130        assert_eq!(result.expect_err("should be err").attached.len(), 1);
131    }
132
133    #[test]
134    fn result_attach__structured_error() {
135        // Arrange
136        let result: Result<i32, StructuredError> = Err(StructuredError::new(OuterError::Operation));
137        // Act
138        let result = result.attach("file", "test.txt");
139        // Assert
140        assert_eq!(result.expect_err("should be err").attached.len(), 1);
141    }
142}