1use codespan_reporting::diagnostic::{Diagnostic, Label};
2use codespan_reporting::files::SimpleFile;
3use codespan_reporting::term::termcolor::StandardStream;
4use codespan_reporting::term::{self, ColorArg};
5use std::ops::Range;
6use structopt::StructOpt;
7
8#[derive(Debug, StructOpt)]
9#[structopt(name = "emit")]
10pub 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
20fn 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
52enum Error {
53    MismatchType(Item, Item),
54    MutatingImmutable(Item, Item),
55}
56
57impl Error {
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.
95struct Item {
96    range: Range<usize>,
97    content: String,
98}
99
100impl Item {
101    fn new(range: Range<usize>, content: impl Into<String>) -> Item {
102        let content = content.into();
103        Item { range, content }
104    }
105}
106