1use crate::common;
11use crate::lexer;
12use crate::rebuilder;
13use crate::resolved;
14
15use serde::Deserialize;
16use std::io::Write;
17use termcolor::{Color, ColorSpec, StandardStream, WriteColor};
18
19#[derive(Debug, Default, Deserialize, clap::Args)]
20#[serde(default, deny_unknown_fields)]
21pub struct Options
22{
23 #[clap(short, long)]
25 verbose: bool,
26
27 #[clap(hide(true), long, value_name("WHEN"))]
29 #[clap(value_enum, default_value_t=ColorChoice::Auto)]
30 color: ColorChoice,
31
32 #[clap(long, value_name("CHARSET"))]
34 #[clap(value_enum, default_value_t=CharSet::Unicode)]
35 arrows: CharSet,
36}
37
38#[derive(Debug, Default, Clone, Copy, Deserialize, clap::ValueEnum)]
39pub enum ColorChoice
40{
41 #[default]
42 Auto,
43 Always,
44 Never,
45}
46
47impl From<ColorChoice> for termcolor::ColorChoice
48{
49 fn from(choice: ColorChoice) -> termcolor::ColorChoice
50 {
51 match choice
52 {
53 ColorChoice::Auto => termcolor::ColorChoice::Auto,
54 ColorChoice::Always => termcolor::ColorChoice::Always,
55 ColorChoice::Never => termcolor::ColorChoice::Never,
56 }
57 }
58}
59
60#[derive(Debug, Default, Clone, Copy, Deserialize, clap::ValueEnum)]
61pub enum CharSet
62{
63 #[default]
64 Unicode,
65 Ascii,
66}
67
68impl From<CharSet> for ariadne::CharSet
69{
70 fn from(choice: CharSet) -> ariadne::CharSet
71 {
72 match choice
73 {
74 CharSet::Unicode => ariadne::CharSet::Unicode,
75 CharSet::Ascii => ariadne::CharSet::Ascii,
76 }
77 }
78}
79
80pub struct StdOut
81{
82 stdout: StandardStream,
83 is_verbose: bool,
84 ariadne_config: ariadne::Config,
85}
86
87impl StdOut
88{
89 pub fn new(options: Options) -> StdOut
90 {
91 let stdout = StandardStream::stdout(options.color.into());
92 let is_verbose = options.verbose;
93 let with_color = match options.color
94 {
95 ColorChoice::Auto => stdout.supports_color(),
96 ColorChoice::Always => true,
97 ColorChoice::Never => false,
98 };
99 let ariadne_config = ariadne::Config::default()
100 .with_color(with_color)
101 .with_char_set(options.arrows.into());
102 StdOut {
103 stdout,
104 is_verbose,
105 ariadne_config,
106 }
107 }
108
109 pub fn newline(&mut self) -> Result<(), std::io::Error>
110 {
111 if self.is_verbose
112 {
113 writeln!(self.stdout)?;
114 }
115 Ok(())
116 }
117
118 pub fn io_header(
119 &mut self,
120 preamble: &str,
121 path: &std::path::Path,
122 ) -> Result<(), std::io::Error>
123 {
124 let colorspec_header = ColorSpec::new();
125 self.stdout.set_color(&colorspec_header)?;
126 writeln!(self.stdout, "{} {}...", preamble, path.to_string_lossy())?;
127 Ok(())
128 }
129
130 pub fn cmd_header(
131 &mut self,
132 preamble: &str,
133 command: String,
134 ) -> Result<(), std::io::Error>
135 {
136 let colorspec_header = ColorSpec::new();
137 self.stdout.set_color(&colorspec_header)?;
138 writeln!(self.stdout)?;
139 writeln!(self.stdout, "{} {}...", preamble, &command)?;
140 Ok(())
141 }
142
143 pub fn header(
144 &mut self,
145 preamble: &str,
146 filename: &str,
147 ) -> Result<(), std::io::Error>
148 {
149 if self.is_verbose
150 {
151 let colorspec_header = ColorSpec::new();
152 self.stdout.set_color(&colorspec_header)?;
153 writeln!(self.stdout, "{} {}...", preamble, filename)?;
154 }
155 Ok(())
156 }
157
158 pub fn basic_header(
159 &mut self,
160 section_name: &str,
161 ) -> Result<(), std::io::Error>
162 {
163 if self.is_verbose
164 {
165 let colorspec_header = ColorSpec::new();
166 self.stdout.set_color(&colorspec_header)?;
167 writeln!(self.stdout, "{}...", section_name)?;
168 }
169 Ok(())
170 }
171
172 pub fn dump_tokens(
173 &mut self,
174 tokens: &[lexer::LexedToken],
175 ) -> Result<(), anyhow::Error>
176 {
177 if self.is_verbose
178 {
179 let colorspec_dump = ColorSpec::new().set_dimmed(true).to_owned();
180 self.stdout.set_color(&colorspec_dump)?;
181 writeln!(self.stdout, "{:?}", tokens)?;
182 writeln!(self.stdout)?;
183 for token in tokens
184 {
185 match &token.result
186 {
187 Result::Ok(token) => write!(self.stdout, "{:?} ", token)?,
188 Result::Err(_) => write!(self.stdout, "ERROR ")?,
189 }
190 }
191 writeln!(self.stdout)?;
192 writeln!(self.stdout)?;
193 }
194 Ok(())
195 }
196
197 pub fn dump_code(
198 &mut self,
199 filename: &str,
200 declarations: &[common::Declaration],
201 ) -> Result<(), anyhow::Error>
202 {
203 if self.is_verbose
204 {
205 let colorspec_dump = ColorSpec::new().set_dimmed(true).to_owned();
206 self.stdout.set_color(&colorspec_dump)?;
207 writeln!(self.stdout, "{:?}", declarations)?;
208 writeln!(self.stdout)?;
209
210 self.header("Rebuilding", filename)?;
211
212 self.stdout.set_color(&colorspec_dump)?;
213 let indentation = rebuilder::Indentation {
214 value: "\u{00a6} ",
215 amount: 1,
216 };
217 let code = rebuilder::rebuild(declarations, &indentation)?;
218 writeln!(self.stdout, "{}", code)?;
219 }
220 Ok(())
221 }
222
223 pub fn dump_resolved(
224 &mut self,
225 _filename: &str,
226 declarations: &[resolved::Declaration],
227 ) -> Result<(), anyhow::Error>
228 {
229 if self.is_verbose
230 {
231 let colorspec_dump = ColorSpec::new().set_dimmed(true).to_owned();
232 self.stdout.set_color(&colorspec_dump)?;
233 writeln!(self.stdout, "{:?}", declarations)?;
234 writeln!(self.stdout)?;
235 }
236 Ok(())
237 }
238
239 pub fn dump_text(&mut self, text: &str) -> Result<(), anyhow::Error>
240 {
241 if self.is_verbose
242 {
243 let colorspec_dump = ColorSpec::new().set_dimmed(true).to_owned();
244 self.stdout.set_color(&colorspec_dump)?;
245 writeln!(self.stdout, "{}", text)?;
246 writeln!(self.stdout)?;
247 }
248 Ok(())
249 }
250
251 pub fn linting(
252 &mut self,
253 was_successful: bool,
254 ) -> Result<(), std::io::Error>
255 {
256 if self.is_verbose
257 {
258 if was_successful
259 {
260 let colorspec_success =
261 ColorSpec::new().set_fg(Some(Color::Green)).to_owned();
262 self.stdout.set_color(&colorspec_success)?;
263 writeln!(self.stdout, "Linting complete.")?;
264 }
265 else
266 {
267 let colorspec_warning = ColorSpec::new()
268 .set_fg(Some(Color::Yellow))
269 .set_bold(true)
270 .to_owned();
271 self.stdout.set_color(&colorspec_warning)?;
272 writeln!(self.stdout, "Linting raised some warnings.")?;
273 }
274 }
275 Ok(())
276 }
277
278 pub fn prepare_for_errors(&mut self) -> Result<(), std::io::Error>
279 {
280 let colorspec_error = ColorSpec::new()
281 .set_fg(Some(Color::Red))
282 .set_bold(true)
283 .to_owned();
284 self.stdout.set_color(&colorspec_error)?;
285 writeln!(self.stdout)?;
286 Ok(())
287 }
288
289 pub fn show_errors(
290 &mut self,
291 errors: impl IntoIterator<Item = crate::error::Error>,
292 mut source_cache: impl ariadne::Cache<String>,
293 ) -> Result<(), std::io::Error>
294 {
295 for error in errors
296 {
297 writeln!(self.stdout)?;
298 let report = error.build_report(self.ariadne_config);
299 report.eprint(&mut source_cache)?;
300 }
301 writeln!(self.stdout)?;
302 Ok(())
303 }
304
305 pub fn output(&mut self, value: i32) -> Result<(), std::io::Error>
306 {
307 self.stdout.reset()?;
308 writeln!(self.stdout, "Output: {}", value)?;
309 Ok(())
310 }
311
312 pub fn done(&mut self) -> Result<(), std::io::Error>
313 {
314 self.stdout.reset()?;
315 writeln!(self.stdout, "Done.")?;
316 Ok(())
317 }
318}