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