taplo_cli/
printing.rs

1use crate::Taplo;
2use codespan_reporting::{
3    diagnostic::{Diagnostic, Label},
4    files::SimpleFile,
5    term::{
6        self,
7        termcolor::{Ansi, NoColor},
8    },
9};
10use itertools::Itertools;
11use std::ops::Range;
12use taplo::{dom, parser, rowan::TextRange};
13use taplo_common::environment::Environment;
14#[cfg(feature = "lint")]
15use taplo_common::schema::NodeValidationError;
16use tokio::io::AsyncWriteExt;
17
18impl<E: Environment> Taplo<E> {
19    pub(crate) async fn print_parse_errors(
20        &self,
21        file: &SimpleFile<&str, &str>,
22        errors: &[parser::Error],
23    ) -> Result<(), anyhow::Error> {
24        let mut out_diag = Vec::<u8>::new();
25
26        let config = codespan_reporting::term::Config::default();
27
28        for error in errors.iter().unique_by(|e| e.range) {
29            let diag = Diagnostic::error()
30                .with_message("invalid TOML")
31                .with_labels(Vec::from([
32                    Label::primary((), std_range(error.range)).with_message(&error.message)
33                ]));
34
35            if self.colors {
36                term::emit(&mut Ansi::new(&mut out_diag), &config, file, &diag)?;
37            } else {
38                term::emit(&mut NoColor::new(&mut out_diag), &config, file, &diag)?;
39            }
40        }
41
42        let mut stderr = self.env.stderr();
43
44        stderr.write_all(&out_diag).await?;
45        stderr.flush().await?;
46
47        Ok(())
48    }
49
50    pub(crate) async fn print_semantic_errors(
51        &self,
52        file: &SimpleFile<&str, &str>,
53        errors: impl Iterator<Item = dom::Error>,
54    ) -> Result<(), anyhow::Error> {
55        let mut out_diag = Vec::<u8>::new();
56
57        let config = codespan_reporting::term::Config::default();
58
59        for error in errors {
60            let diag = match &error {
61                dom::Error::ConflictingKeys { key, other } => Diagnostic::error()
62                    .with_message(error.to_string())
63                    .with_labels(Vec::from([
64                        Label::primary((), std_range(key.text_ranges().next().unwrap()))
65                            .with_message("duplicate key"),
66                        Label::secondary((), std_range(other.text_ranges().next().unwrap()))
67                            .with_message("duplicate found here"),
68                    ])),
69                dom::Error::ExpectedArrayOfTables {
70                    not_array_of_tables,
71                    required_by,
72                } => Diagnostic::error()
73                    .with_message(error.to_string())
74                    .with_labels(Vec::from([
75                        Label::primary(
76                            (),
77                            std_range(not_array_of_tables.text_ranges().next().unwrap()),
78                        )
79                        .with_message("expected array of tables"),
80                        Label::secondary((), std_range(required_by.text_ranges().next().unwrap()))
81                            .with_message("required by this key"),
82                    ])),
83                dom::Error::ExpectedTable {
84                    not_table,
85                    required_by,
86                } => Diagnostic::error()
87                    .with_message(error.to_string())
88                    .with_labels(Vec::from([
89                        Label::primary((), std_range(not_table.text_ranges().next().unwrap()))
90                            .with_message("expected table"),
91                        Label::secondary((), std_range(required_by.text_ranges().next().unwrap()))
92                            .with_message("required by this key"),
93                    ])),
94                dom::Error::InvalidEscapeSequence { string } => Diagnostic::error()
95                    .with_message(error.to_string())
96                    .with_labels(Vec::from([Label::primary(
97                        (),
98                        std_range(string.text_range()),
99                    )
100                    .with_message("the string contains invalid escape sequences")])),
101                _ => {
102                    unreachable!("this is a bug")
103                }
104            };
105
106            if self.colors {
107                term::emit(&mut Ansi::new(&mut out_diag), &config, file, &diag)?;
108            } else {
109                term::emit(&mut NoColor::new(&mut out_diag), &config, file, &diag)?;
110            }
111        }
112        let mut stderr = self.env.stderr();
113        stderr.write_all(&out_diag).await?;
114        stderr.flush().await?;
115        Ok(())
116    }
117
118    #[cfg(feature = "lint")]
119    pub(crate) async fn print_schema_errors(
120        &self,
121        file: &SimpleFile<&str, &str>,
122        errors: &[NodeValidationError],
123    ) -> Result<(), anyhow::Error> {
124        let config = codespan_reporting::term::Config::default();
125
126        let mut out_diag = Vec::<u8>::new();
127        for err in errors {
128            let msg = err.error.to_string();
129            for text_range in err.text_ranges() {
130                let diag = Diagnostic::error()
131                    .with_message(err.error.to_string())
132                    .with_labels(Vec::from([
133                        Label::primary((), std_range(text_range)).with_message(&msg)
134                    ]));
135
136                if self.colors {
137                    term::emit(&mut Ansi::new(&mut out_diag), &config, file, &diag)?;
138                } else {
139                    term::emit(&mut NoColor::new(&mut out_diag), &config, file, &diag)?;
140                };
141            }
142        }
143        let mut stderr = self.env.stderr();
144        stderr.write_all(&out_diag).await?;
145        stderr.flush().await?;
146
147        Ok(())
148    }
149}
150
151fn std_range(range: TextRange) -> Range<usize> {
152    let start: usize = u32::from(range.start()) as _;
153    let end: usize = u32::from(range.end()) as _;
154    start..end
155}