1 use codespan_reporting::diagnostic::{Diagnostic, Label};
2 use codespan_reporting::files::SimpleFile;
3 use codespan_reporting::term::termcolor::StandardStream;
4 use codespan_reporting::term::{self, ColorArg};
5 use std::ops::Range;
6 use structopt::StructOpt;
7 
8 #[derive(Debug, StructOpt)]
9 #[structopt(name = "emit")]
10 pub struct Opts {
11     #[structopt(long = "color",
12         parse(try_from_str),
13         default_value = "auto",
14         possible_values = ColorArg::VARIANTS,
15         case_insensitive = true
16     )]
17     color: ColorArg,
18 }
19 
main() -> anyhow::Result<()>20 fn main() -> anyhow::Result<()> {
21     let file = SimpleFile::new(
22         "main.rs",
23         unindent::unindent(
24             r#"
25                 fn main() {
26                     let foo: i32 = "hello, world";
27                     foo += 1;
28                 }
29             "#,
30         ),
31     );
32 
33     let errors = [
34         Error::MismatchType(
35             Item::new(20..23, "i32"),
36             Item::new(31..45, "\"hello, world\""),
37         ),
38         Error::MutatingImmutable(Item::new(20..23, "foo"), Item::new(51..59, "foo += 1")),
39     ];
40 
41     let opts = Opts::from_args();
42     let writer = StandardStream::stderr(opts.color.into());
43     let config = codespan_reporting::term::Config::default();
44     for diagnostic in errors.iter().map(Error::report) {
45         term::emit(&mut writer.lock(), &config, &file, &diagnostic)?;
46     }
47 
48     Ok(())
49 }
50 
51 /// An error enum that represent all possible errors within your program
52 enum Error {
53     MismatchType(Item, Item),
54     MutatingImmutable(Item, Item),
55 }
56 
57 impl Error {
report(&self) -> Diagnostic<()>58     fn report(&self) -> Diagnostic<()> {
59         match self {
60             Error::MismatchType(left, right) => Diagnostic::error()
61                 .with_code("E0308")
62                 .with_message("mismatch types")
63                 .with_labels(vec![
64                     Label::primary((), right.range.clone()).with_message(format!(
65                         "Expected `{}`, found: `{}`",
66                         left.content, right.content,
67                     )),
68                     Label::secondary((), left.range.clone()).with_message("expected due to this"),
69                 ]),
70             Error::MutatingImmutable(original, mutating) => Diagnostic::error()
71                 .with_code("E0384")
72                 .with_message(format!(
73                     "cannot mutate immutable variable `{}`",
74                     original.content,
75                 ))
76                 .with_labels(vec![
77                     Label::secondary((), original.range.clone()).with_message(unindent::unindent(
78                         &format!(
79                             r#"
80                                 first assignment to `{0}`
81                                 help: make this binding mutable: `mut {0}`
82                             "#,
83                             original.content,
84                         ),
85                     )),
86                     Label::primary((), mutating.range.clone())
87                         .with_message("cannot assign twice to immutable variable"),
88                 ]),
89         }
90     }
91 }
92 
93 /// An item in the source code to be used in the `Error` enum.
94 /// In a more complex program it could also contain a `files::FileId` to handle errors that occur inside multiple files.
95 struct Item {
96     range: Range<usize>,
97     content: String,
98 }
99 
100 impl Item {
new(range: Range<usize>, content: impl Into<String>) -> Item101     fn new(range: Range<usize>, content: impl Into<String>) -> Item {
102         let content = content.into();
103         Item { range, content }
104     }
105 }
106