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
// Copyright 2017-2020 the Tectonic Project
// Licensed under the MIT License.

//! A basic status-reporting backend that prints messages via stdio.

use std::{
    fmt::Arguments,
    io::{self, Write},
};
use tectonic_errors::Error;

use super::{ChatterLevel, MessageKind, StatusBackend};

/// A basic status-reporting backend that prints messages via stdio.
#[derive(Clone, Debug, Default)]
pub struct PlainStatusBackend {
    chatter: ChatterLevel,
    always_stderr: bool,
}

impl PlainStatusBackend {
    /// Create a new backend with the specified chatter level.
    ///
    /// To use the default chatter level, you can also use [`Self::default`].
    pub fn new(chatter: ChatterLevel) -> Self {
        PlainStatusBackend {
            chatter,
            always_stderr: false,
        }
    }

    /// Configure this backend to always print to the standard error stream.
    ///
    /// This setting can be useful if you have a program that is printing output
    /// to standard output that needs to be machine-parsable. By activating it
    /// you can ensure that any status reports don't get mixed in with your
    /// stdout output.
    pub fn always_stderr(&mut self, setting: bool) -> &mut Self {
        self.always_stderr = setting;
        self
    }
}

impl StatusBackend for PlainStatusBackend {
    fn report(&mut self, kind: MessageKind, args: Arguments, err: Option<&Error>) {
        if self.chatter.suppress_message(kind) {
            return;
        }

        let prefix = match kind {
            MessageKind::Note => "note:",
            MessageKind::Warning => "warning:",
            MessageKind::Error => "error:",
        };

        if kind == MessageKind::Note && !self.always_stderr {
            println!("{prefix} {args}");
        } else {
            eprintln!("{prefix} {args}");
        }

        if let Some(e) = err {
            for item in e.chain() {
                eprintln!("caused by: {item}");
            }
        }
    }

    fn report_error(&mut self, err: &Error) {
        let mut prefix = "error";

        for item in err.chain() {
            eprintln!("{prefix}: {item}");
            prefix = "caused by";
        }
    }

    fn note_highlighted(&mut self, before: &str, highlighted: &str, after: &str) {
        self.report(
            MessageKind::Note,
            format_args!("{before}{highlighted}{after}"),
            None,
        );
    }

    fn dump_error_logs(&mut self, output: &[u8]) {
        eprintln!(
            "==============================================================================="
        );

        io::stderr()
            .write_all(output)
            .expect("write to stderr failed");

        eprintln!(
            "==============================================================================="
        );
    }
}