1 //! Terminal back-end for emitting diagnostics.
2 
3 use std::str::FromStr;
4 use termcolor::{ColorChoice, WriteColor};
5 
6 use crate::diagnostic::Diagnostic;
7 use crate::files::Files;
8 
9 mod config;
10 mod renderer;
11 mod views;
12 
13 pub use termcolor;
14 
15 pub use self::config::{Chars, Config, DisplayStyle, Styles};
16 
17 /// A command line argument that configures the coloring of the output.
18 ///
19 /// This can be used with command line argument parsers like [`clap`] or [`structopt`].
20 ///
21 /// [`clap`]: https://crates.io/crates/clap
22 /// [`structopt`]: https://crates.io/crates/structopt
23 ///
24 /// # Example
25 ///
26 /// ```rust
27 /// use codespan_reporting::term::termcolor::StandardStream;
28 /// use codespan_reporting::term::ColorArg;
29 /// use structopt::StructOpt;
30 ///
31 /// #[derive(Debug, StructOpt)]
32 /// #[structopt(name = "groovey-app")]
33 /// pub struct Opts {
34 ///     /// Configure coloring of output
35 ///     #[structopt(
36 ///         long = "color",
37 ///         default_value = "auto",
38 ///         possible_values = ColorArg::VARIANTS,
39 ///         case_insensitive = true,
40 ///     )]
41 ///     pub color: ColorArg,
42 /// }
43 ///
44 /// let opts = Opts::from_args();
45 /// let writer = StandardStream::stderr(opts.color.into());
46 /// ```
47 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
48 pub struct ColorArg(pub ColorChoice);
49 
50 impl ColorArg {
51     /// Allowed values the argument.
52     ///
53     /// This is useful for generating documentation via [`clap`] or `structopt`'s
54     /// `possible_values` configuration.
55     ///
56     /// [`clap`]: https://crates.io/crates/clap
57     /// [`structopt`]: https://crates.io/crates/structopt
58     pub const VARIANTS: &'static [&'static str] = &["auto", "always", "ansi", "never"];
59 }
60 
61 impl FromStr for ColorArg {
62     type Err = &'static str;
63 
from_str(src: &str) -> Result<ColorArg, &'static str>64     fn from_str(src: &str) -> Result<ColorArg, &'static str> {
65         match src {
66             _ if src.eq_ignore_ascii_case("auto") => Ok(ColorArg(ColorChoice::Auto)),
67             _ if src.eq_ignore_ascii_case("always") => Ok(ColorArg(ColorChoice::Always)),
68             _ if src.eq_ignore_ascii_case("ansi") => Ok(ColorArg(ColorChoice::AlwaysAnsi)),
69             _ if src.eq_ignore_ascii_case("never") => Ok(ColorArg(ColorChoice::Never)),
70             _ => Err("valid values: auto, always, ansi, never"),
71         }
72     }
73 }
74 
75 impl Into<ColorChoice> for ColorArg {
into(self) -> ColorChoice76     fn into(self) -> ColorChoice {
77         self.0
78     }
79 }
80 
81 /// Emit a diagnostic using the given writer, context, config, and files.
82 ///
83 /// The return value covers all error cases. These error case can arise if:
84 /// * a file was removed from the file database.
85 /// * a file was changed so that it is too small to have an index
86 /// * IO fails
emit<'files, F: Files<'files>>( writer: &mut dyn WriteColor, config: &Config, files: &'files F, diagnostic: &Diagnostic<F::FileId>, ) -> Result<(), super::files::Error>87 pub fn emit<'files, F: Files<'files>>(
88     writer: &mut dyn WriteColor,
89     config: &Config,
90     files: &'files F,
91     diagnostic: &Diagnostic<F::FileId>,
92 ) -> Result<(), super::files::Error> {
93     use self::renderer::Renderer;
94     use self::views::{RichDiagnostic, ShortDiagnostic};
95 
96     let mut renderer = Renderer::new(writer, config);
97     match config.display_style {
98         DisplayStyle::Rich => RichDiagnostic::new(diagnostic, config).render(files, &mut renderer),
99         DisplayStyle::Medium => ShortDiagnostic::new(diagnostic, true).render(files, &mut renderer),
100         DisplayStyle::Short => ShortDiagnostic::new(diagnostic, false).render(files, &mut renderer),
101     }
102 }
103 
104 #[cfg(test)]
105 mod tests {
106     use super::*;
107 
108     use crate::diagnostic::Label;
109     use crate::files::SimpleFiles;
110 
111     #[test]
unsized_emit()112     fn unsized_emit() {
113         let mut files = SimpleFiles::new();
114 
115         let id = files.add("test", "");
116         let mut writer = termcolor::NoColor::new(Vec::<u8>::new());
117         let diagnostic = Diagnostic::bug().with_labels(vec![Label::primary(id, 0..0)]);
118 
119         emit(&mut writer, &Config::default(), &files, &diagnostic).unwrap();
120     }
121 }
122