aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/console/html_output_formatter.rs
blob: a0099e5c2b5ddd6880c309788f7ecc94edaed6e9 (plain)
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
100
//! ref: composer/src/Composer/Console/HtmlOutputFormatter.php

use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::symfony::console::formatter::output_formatter::OutputFormatter;
use shirabe_external_packages::symfony::console::formatter::output_formatter_style::OutputFormatterStyle;

#[derive(Debug)]
pub struct HtmlOutputFormatter {
    inner: OutputFormatter,
}

impl HtmlOutputFormatter {
    const AVAILABLE_FOREGROUND_COLORS: &'static [(i64, &'static str)] = &[
        (30, "black"),
        (31, "red"),
        (32, "green"),
        (33, "yellow"),
        (34, "blue"),
        (35, "magenta"),
        (36, "cyan"),
        (37, "white"),
    ];

    const AVAILABLE_BACKGROUND_COLORS: &'static [(i64, &'static str)] = &[
        (40, "black"),
        (41, "red"),
        (42, "green"),
        (43, "yellow"),
        (44, "blue"),
        (45, "magenta"),
        (46, "cyan"),
        (47, "white"),
    ];

    const AVAILABLE_OPTIONS: &'static [(i64, &'static str)] = &[
        (1, "bold"),
        (4, "underscore"),
        //5 => "blink",
        //7 => "reverse",
        //8 => "conceal"
    ];

    pub fn new(styles: IndexMap<String, OutputFormatterStyle>) -> Self {
        // TODO(phase-b): styles dropped until base OutputFormatter::new accepts a style map
        let _ = styles;
        Self {
            inner: OutputFormatter::new(true),
        }
    }

    pub fn format(&self, message: Option<&str>) -> Option<String> {
        let formatted = self.inner.format(message.unwrap_or(""));

        let clear_escape_codes = "(?:39|49|0|22|24|25|27|28)";
        let pattern = format!(
            "{{\\033\\[([0-9;]+)m(.*?)\\033\\[(?:{};)*?{}m}}s",
            clear_escape_codes, clear_escape_codes
        );

        Preg::replace_callback(&pattern, |matches| self.format_html(matches), &formatted).ok()
    }

    fn format_html(&self, matches: &IndexMap<CaptureKey, String>) -> String {
        let codes_str = matches
            .get(&CaptureKey::ByIndex(1))
            .map(|s| s.as_str())
            .unwrap_or("");
        let content = matches
            .get(&CaptureKey::ByIndex(2))
            .map(|s| s.as_str())
            .unwrap_or("");
        let mut out = String::from("<span style=\"");

        for code_str in codes_str.split(';') {
            let code: i64 = code_str.parse().unwrap_or(0);
            if let Some(&(_, color)) = Self::AVAILABLE_FOREGROUND_COLORS
                .iter()
                .find(|&&(k, _)| k == code)
            {
                out.push_str(&format!("color:{};", color));
            } else if let Some(&(_, color)) = Self::AVAILABLE_BACKGROUND_COLORS
                .iter()
                .find(|&&(k, _)| k == code)
            {
                out.push_str(&format!("background-color:{};", color));
            } else if let Some(&(_, option)) =
                Self::AVAILABLE_OPTIONS.iter().find(|&&(k, _)| k == code)
            {
                match option {
                    "bold" => out.push_str("font-weight:bold;"),
                    "underscore" => out.push_str("text-decoration:underline;"),
                    _ => {}
                }
            }
        }

        format!("{}\">{}</span>", out, content)
    }
}