1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use codespan_reporting::diagnostic::Diagnostic;
use codespan_reporting::files::{Error, SimpleFile};
use std::rc::Rc;

/// Codebase. A struct that holds all your code in memory (codespan forces this)
#[derive(Debug, Default)]
pub struct Codebase {
    config: codespan_reporting::term::Config,
    files: Vec<SimpleFile<String, Rc<str>>>,
    errors: usize,
    warnings: usize,
}

impl Codebase {
    /// Create a new codebase.
    pub fn new() -> Self {
        Self::default()
    }

    /// Add a file to the codebase, returning the handle that can be used to
    /// refer to it again.
    pub fn add(&mut self, name: String, source: String) -> usize {
        let file_id = self.files.len();
        self.files.push(SimpleFile::new(name, Rc::from(source)));
        file_id
    }

    /// Get the file corresponding to the given id.
    pub fn get(&self, file_id: usize) -> Result<&SimpleFile<String, Rc<str>>, Error> {
        self.files.get(file_id).ok_or(Error::FileMissing)
    }

    /// Emit a diagnostic
    pub fn emit(&mut self, diagnostic: Diagnostic<usize>) {
        match diagnostic.severity {
            codespan_reporting::diagnostic::Severity::Bug => (),
            codespan_reporting::diagnostic::Severity::Error => self.errors += 1,
            codespan_reporting::diagnostic::Severity::Warning => self.warnings += 1,
            codespan_reporting::diagnostic::Severity::Note => (),
            codespan_reporting::diagnostic::Severity::Help => (),
        };
        let mut writer = codespan_reporting::term::termcolor::StandardStream::stderr(
            codespan_reporting::term::termcolor::ColorChoice::Auto,
        );
        codespan_reporting::term::emit(&mut writer, &self.config, self, &diagnostic)
            .expect("internal error");
    }

    pub fn files(&self) -> &Vec<SimpleFile<String, Rc<str>>> {
        &self.files
    }

    /// Get the number of errors emitted
    pub fn errors(&self) -> usize {
        self.errors
    }

    /// Get the number of warnings emitted
    pub fn warnings(&self) -> usize {
        self.warnings
    }
}

impl<'a> codespan_reporting::files::Files<'a> for Codebase {
    type FileId = usize;
    type Name = String;
    type Source = &'a str;

    fn name(&self, file_id: usize) -> Result<String, Error> {
        Ok(self.get(file_id)?.name().clone())
    }

    fn source(&self, file_id: usize) -> Result<&str, Error> {
        Ok(self.get(file_id)?.source().as_ref())
    }

    fn line_index(&self, file_id: usize, byte_index: usize) -> Result<usize, Error> {
        self.get(file_id)?.line_index((), byte_index)
    }

    fn line_range(
        &self,
        file_id: usize,
        line_index: usize,
    ) -> Result<std::ops::Range<usize>, Error> {
        self.get(file_id)?.line_range((), line_index)
    }
}