try_drop/drop_strategies/write/
thread_unsafe.rs

1use crate::FallibleTryDropStrategy;
2use anyhow::Error;
3use std::cell::RefCell;
4use std::io;
5use std::io::Write;
6use std::string::ToString;
7use std::vec::Vec;
8
9/// A drop strategy which writes the message of an error to a writer. While more efficient than
10/// it's thread safe counterpart, it's less flexible.
11#[cfg_attr(
12    feature = "derives",
13    derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)
14)]
15pub struct ThreadUnsafeWriteDropStrategy<W: Write> {
16    /// The writer to write to.
17    pub writer: RefCell<W>,
18
19    /// Whether or not to append a newline to the end of the message.
20    pub new_line: bool,
21
22    /// The message to add at the beginning of the message.
23    pub prelude: Option<Vec<u8>>,
24}
25
26impl<W: Write> ThreadUnsafeWriteDropStrategy<W> {
27    /// Creates a new [`ThreadUnsafeWriteDropStrategy`] with the given writer.
28    pub fn new(writer: W) -> Self {
29        Self {
30            writer: RefCell::new(writer),
31            new_line: true,
32            prelude: None,
33        }
34    }
35
36    /// Sets whether or not to append a newline to the end of the message.
37    pub fn new_line(&mut self, new_line: bool) -> &mut Self {
38        self.new_line = new_line;
39        self
40    }
41
42    /// Sets the message to add at the beginning of the message.
43    pub fn prelude(&mut self, prelude: impl Into<Vec<u8>>) -> &mut Self {
44        self.prelude = Some(prelude.into());
45        self
46    }
47}
48
49impl ThreadUnsafeWriteDropStrategy<io::Stderr> {
50    /// Write to standard error.
51    pub fn stderr() -> Self {
52        Self::new(io::stderr())
53    }
54}
55
56impl ThreadUnsafeWriteDropStrategy<io::Stdout> {
57    /// Write to standard output.
58    pub fn stdout() -> Self {
59        Self::new(io::stdout())
60    }
61}
62
63impl<W: Write> FallibleTryDropStrategy for ThreadUnsafeWriteDropStrategy<W> {
64    type Error = io::Error;
65
66    fn try_handle_error(&self, error: Error) -> Result<(), Self::Error> {
67        let mut message = Vec::new();
68
69        if let Some(prelude) = &self.prelude {
70            message.extend_from_slice(prelude);
71        }
72
73        message.extend_from_slice(error.to_string().as_bytes());
74
75        if self.new_line {
76            message.push(b'\n')
77        }
78
79        self.writer.borrow_mut().write_all(&message)
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use crate::drop_strategies::PanicDropStrategy;
87    use std::io::Cursor;
88    use crate::test_utils::fallible_given;
89
90    #[test]
91    fn test_write_drop_strategy() {
92        let mut writer = Cursor::new(Vec::new());
93        let mut strategy = ThreadUnsafeWriteDropStrategy::new(&mut writer);
94        strategy.prelude("error: ");
95        drop(fallible_given(strategy, PanicDropStrategy::default()));
96        assert_eq!(writer.into_inner(), b"error: this will always fail\n",)
97    }
98}
99