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}