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