tiger_lib/report/
output_style.rs

1use std::collections::HashMap;
2
3use ansiterm::Colour::{Black, Blue, Cyan, Green, Purple, Red, White, Yellow};
4use ansiterm::Style;
5
6use crate::report::Severity;
7
8/// For looking up the style to use for the various parts of the output.
9#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq)]
10pub enum Styled {
11    #[default]
12    Default,
13    Tag(Severity, IsTag),
14    /// The actual error message, telling the user what is wrong.
15    ErrorMessage,
16    /// Introduces additional info on a report.
17    InfoTag,
18    /// The actual info message. Optionally attached to a report.
19    Info,
20    /// Filename, line number, column number.
21    Location,
22    /// The caret, pointing at the exact location of the error.
23    /// TODO: Maybe this should depend on error level.
24    Caret,
25    /// Text from the source file.
26    /// By default, this should probably be unstyled,
27    /// but we might want to add the option to configure it.
28    SourceText,
29    /// We could potentially give emphasis to certain parts of an error message.
30    /// For example; in the following message, the word limit could perhaps be italicized.
31    /// "required field `limit` missing"
32    Emphasis,
33}
34
35/// Whether the style applies to the `ErrorLevel` tag itself or the `ErrorKey` that follows it.
36pub type IsTag = bool;
37
38#[derive(Debug)]
39pub struct OutputStyle {
40    map: HashMap<Styled, Style>,
41}
42
43impl Default for OutputStyle {
44    /// Constructs an instance of `OutputStyles` that uses default, hard-coded color values.
45    fn default() -> Self {
46        let mut map = HashMap::new();
47        map.insert(Styled::Default, Style::new());
48
49        map.insert(Styled::InfoTag, Style::new().bold());
50        map.insert(Styled::Info, Style::new());
51        map.insert(Styled::ErrorMessage, Style::new().bold());
52        map.insert(Styled::Location, Blue.bold());
53        map.insert(Styled::Caret, Style::new().bold());
54        map.insert(Styled::SourceText, Style::new());
55        map.insert(Styled::Emphasis, Style::new().italic());
56
57        map.insert(Styled::Tag(Severity::Fatal, true), White.bold());
58        map.insert(Styled::Tag(Severity::Fatal, false), White.bold());
59        map.insert(Styled::Tag(Severity::Error, true), Red.bold());
60        map.insert(Styled::Tag(Severity::Error, false), Red.bold());
61        map.insert(Styled::Tag(Severity::Warning, true), Yellow.bold());
62        map.insert(Styled::Tag(Severity::Warning, false), Yellow.normal());
63        map.insert(Styled::Tag(Severity::Untidy, true), Cyan.bold());
64        map.insert(Styled::Tag(Severity::Untidy, false), Cyan.normal());
65        map.insert(Styled::Tag(Severity::Tips, true), Green.bold());
66        map.insert(Styled::Tag(Severity::Tips, false), Green.normal());
67
68        OutputStyle { map }
69    }
70}
71
72impl OutputStyle {
73    /// Construct a version of the `OutputStyles` that always returns the default, no-colour style.
74    /// Use this to effectively disable any ANSI characters in the output.
75    pub fn no_color() -> Self {
76        let mut map = HashMap::new();
77        map.insert(Styled::Default, Style::new());
78        OutputStyle { map }
79    }
80    pub fn style(&self, output: Styled) -> &Style {
81        self.map
82            .get(&output)
83            .or_else(|| self.map.get(&Styled::Default))
84            .expect("Failed to retrieve output style.")
85    }
86    /// Allows overriding a color for a given `ErrorLevel`.
87    pub fn set(&mut self, severity: Severity, color_str: &str) {
88        if let Some(color) = match color_str.to_ascii_lowercase().as_str() {
89            "black" => Some(Black),
90            "red" => Some(Red),
91            "green" => Some(Green),
92            "yellow" => Some(Yellow),
93            "blue" => Some(Blue),
94            "purple" => Some(Purple),
95            "cyan" => Some(Cyan),
96            "white" => Some(White),
97            _ => None,
98        } {
99            self.map.insert(Styled::Tag(severity, true), color.bold());
100            self.map.insert(Styled::Tag(severity, false), color.normal());
101        } else {
102            eprintln!(
103                "Tried to set ErrorLevel::{severity} to color {color_str}, but that color was not recognised! Defaulting to regular color instead.\nSupported colors are Black, Red, Green, Yellow, Blue, Purple, Cyan, White."
104            );
105        }
106    }
107}