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
89
pub use codespan_reporting::diagnostic::Severity;
use codespan_reporting::diagnostic::{Diagnostic, Label};
#[cfg(feature = "size-of")]
use size_of::SizeOf;

use kodept_core::code_point::CodePoint;
use kodept_core::file_relative::CodePath;

#[derive(Debug)]
pub struct ReportMessage {
    pub severity: Severity,
    pub code: String,
    pub message: String,
    pub additional_message: String,
}

#[derive(Debug)]
pub struct Report {
    diagnostic: Diagnostic<()>,
}

impl ReportMessage {
    pub fn new<S: Into<String>>(severity: Severity, code: S, message: String) -> Self {
        Self {
            severity,
            code: code.into(),
            message,
            additional_message: "here".to_string(),
        }
    }

    #[must_use]
    pub fn with_additional_message(self, additional_message: String) -> Self {
        Self {
            additional_message,
            ..self
        }
    }
}

impl Report {
    #[must_use]
    pub const fn is_error(&self) -> bool {
        matches!(self.diagnostic.severity, Severity::Error | Severity::Bug)
    }

    pub fn new<R: Into<ReportMessage>>(
        _file: &CodePath,
        points: Vec<CodePoint>,
        message: R,
    ) -> Self {
        let msg = message.into();
        let diagnostic = Diagnostic::new(msg.severity)
            .with_code(msg.code)
            .with_message(msg.message);
        let diagnostic = if let [p] = points.as_slice() {
            diagnostic.with_labels(vec![Label::primary((), p.as_range())])
        } else if let [p, s @ ..] = points.as_slice() {
            let mut secondaries: Vec<_> = s
                .iter()
                .map(|it| Label::secondary((), it.as_range()))
                .collect();
            secondaries.insert(0, Label::primary((), p.as_range()));
            diagnostic.with_labels(secondaries)
        } else {
            diagnostic
        };
        Self { diagnostic }
    }

    pub fn into_diagnostic(self) -> Diagnostic<()> {
        self.diagnostic
    }
}

#[cfg(feature = "size-of")]
impl SizeOf for Report {
    fn size_of_children(&self, context: &mut size_of::Context) {
        context.add(1); // severity
        self.diagnostic.message.size_of_children(context);
        self.diagnostic.code.size_of_children(context);
        self.diagnostic.notes.size_of_children(context);
        context.add_vectorlike(
            self.diagnostic.labels.len(),
            self.diagnostic.labels.capacity(),
            std::mem::size_of::<Label<()>>(),
        );
    }
}