mit_commit_message_lints/console/
style.rs

1//! Visual styling for the output
2
3use std::fmt::Display;
4
5use comfy_table::{
6    modifiers::UTF8_ROUND_CORNERS,
7    presets::UTF8_FULL,
8    Attribute,
9    Cell,
10    ContentArrangement,
11    Table,
12};
13use miette::{Diagnostic, GraphicalReportHandler, Severity};
14use mit_lint::Lints;
15use thiserror::Error;
16
17use crate::mit::Authors;
18
19/// Print a advice using our error handler tool
20///
21/// # Panics
22///
23/// Panics on a format failure. This should be impossible
24pub fn success(success: &str, tip: &str) {
25    let mut out = String::new();
26    GraphicalReportHandler::default()
27        .render_report(
28            &mut out,
29            &Success {
30                success: success.to_string(),
31                help: Some(tip),
32            },
33        )
34        .unwrap();
35    println!("{out}");
36}
37
38#[derive(Error, Debug)]
39#[error("{success}")]
40struct Success<'a> {
41    success: String,
42    help: Option<&'a str>,
43}
44
45impl Diagnostic for Success<'_> {
46    fn severity(&self) -> Option<Severity> {
47        Some(Severity::Advice)
48    }
49
50    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
51        self.help
52            .map(|x| Box::new(x.to_string()) as Box<dyn Display>)
53    }
54}
55
56#[derive(Error, Debug)]
57#[error("{warning}")]
58struct Warning<'a> {
59    warning: String,
60    help: Option<&'a str>,
61}
62
63impl Diagnostic for Warning<'_> {
64    fn severity(&self) -> Option<Severity> {
65        Some(Severity::Warning)
66    }
67
68    fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
69        self.help
70            .map(|x| Box::new(x.to_string()) as Box<dyn Display>)
71    }
72}
73
74/// Print a warning using our error handler tool
75///
76/// # Panics
77///
78/// Panics on a format failure. This should be impossible
79pub fn warning(warning: &str, tip: Option<&str>) {
80    let mut out = String::new();
81    GraphicalReportHandler::default()
82        .render_report(
83            &mut out,
84            &Warning {
85                warning: warning.to_string(),
86                help: tip,
87            },
88        )
89        .unwrap();
90    eprintln!("{out}");
91}
92
93/// Print entirely undecorated to stdout
94pub fn to_be_piped(output: &str) {
95    println!("{output}");
96}
97
98/// Print a table of lints
99pub fn lint_table(list: &Lints, enabled: &Lints) {
100    let mut table = Table::new();
101    table
102        .load_preset(UTF8_FULL)
103        .apply_modifier(UTF8_ROUND_CORNERS)
104        .set_content_arrangement(ContentArrangement::Dynamic)
105        .set_header(vec!["Lint", "Status"]);
106
107    let rows: Table = list.clone().into_iter().fold(table, |mut table, lint| {
108        table.add_row(vec![
109            lint.name(),
110            if enabled.clone().into_iter().any(|x| x == lint) {
111                "enabled"
112            } else {
113                "disabled"
114            },
115        ]);
116        table
117    });
118
119    println!("{rows}");
120}
121
122/// Print a table of authors
123#[must_use]
124pub fn author_table(authors: &Authors<'_>) -> String {
125    let mut table = Table::new();
126    table
127        .load_preset(UTF8_FULL)
128        .apply_modifier(UTF8_ROUND_CORNERS)
129        .set_content_arrangement(ContentArrangement::Dynamic)
130        .set_header(vec!["Initial", "Name", "Email", "Signing Key"]);
131
132    let rows: Table = authors
133        .clone()
134        .into_iter()
135        .fold(table, |mut table, (initial, author)| {
136            table.add_row(vec![
137                Cell::new(initial),
138                Cell::new(author.name()),
139                Cell::new(author.email()),
140                author.signingkey().map_or_else(
141                    || Cell::new("None".to_string()).add_attributes(vec![Attribute::Italic]),
142                    Cell::new,
143                ),
144            ]);
145            table
146        });
147
148    format!("{rows}")
149}