dd_lib/io/
errhandler.rs

1use opts;
2use results::elapsed;
3use std::{
4    io::{Result, Write},
5    time::SystemTime,
6};
7use units;
8
9/// ErrHandler handles errors and progress information by writing to it's
10/// internal writer.
11pub struct ErrHandler<E: std::io::Write> {
12    /// the internal writer (almost always [`Stderr`][std::io::StdErr])
13    e: E,
14    /// whether to make continual progress reports. See
15    /// [opts::StatusLevel::Report]
16    report: bool,
17    /// Whether to continue on an io error. see [opts::Conv]
18    continue_on_err: bool,
19
20    /// when the previous report happened (in bytes, lines, or blcoks)
21    prev_report: usize,
22}
23
24impl<E: Write> ErrHandler<E> {
25    pub fn new(e: E, o: &opts::Opts) -> Self {
26        let report = o.status == opts::StatusLevel::Progress;
27        let continue_on_err = o.cflag(opts::CFlag::NOERROR);
28        ErrHandler {
29            e,
30            report,
31            continue_on_err,
32            prev_report: 0,
33        }
34    }
35}
36impl<E: Write> ErrHandler<E> {
37    /// threshold of blocks written per report.
38    pub const BLOCK_THRESHOLD_FOR_REPORT: usize = 25;
39    /// threshold of lines written per report.
40    pub const LINE_THRESHOLD_FOR_REPORT: usize = 20;
41    /// threshold of bytes written per erport
42    pub const STANDARD_BYTE_THRESHOLD: usize = 5 * units::MB;
43
44    /// handle an error, promoting it unless `continue_on_error` is set.
45    pub fn handle<T>(&mut self, res: Result<T>) -> Result<Option<T>> {
46        match (res, self.continue_on_err) {
47            (Err(err), true) => {
48                writeln!(self.e, "io error: <{}>; continuing", err);
49                Ok(None)
50            },
51            (Err(err), false) => Err(err),
52            (Ok(t), _) => Ok(Some(t)),
53        }
54    }
55
56    /// report status during standard operation
57    pub fn report_status_standard(&mut self, bytes: usize, start: &SystemTime) -> std::io::Result<()> {
58        if self.report && bytes.saturating_sub(self.prev_report) > Self::STANDARD_BYTE_THRESHOLD {
59            self.prev_report = bytes;
60            writeln!(
61                self,
62                "dd in progress: elapsed: {}: wrote {} bytes",
63                elapsed(start),
64                bytes
65            )?;
66        }
67        Ok(())
68    }
69
70    pub fn report_status_block(&mut self, lines: usize, start: &SystemTime) {
71        if self.report && lines % Self::LINE_THRESHOLD_FOR_REPORT == 0 {
72            writeln!(
73                self,
74                "dd in progress: unblock: elapsed: {}, wrote {} lines",
75                elapsed(&start),
76                lines
77            );
78        }
79    }
80
81    pub fn report_status_unblock(&mut self, blocks: usize, start: &SystemTime) {
82        if self.report && blocks % Self::BLOCK_THRESHOLD_FOR_REPORT == 0 {
83            writeln!(
84                self,
85                "dd in progress: unblock: elapsed: {}, wrote {} fixed-length blocks",
86                elapsed(&start),
87                blocks
88            );
89        }
90    }
91}
92impl<E: Write> Write for ErrHandler<E> {
93    fn write(&mut self, buf: &[u8]) -> Result<usize> { self.e.write(buf) }
94
95    fn flush(&mut self) -> Result<()> { self.e.flush() }
96}