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