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