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 90 91 92 93 94 95
//! Reporting errors, warnings, and other information to the user.
use crate::parse::FileId;
use std::{convert::TryInto, ops::Range};
/// A span of a source file.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
    start: u32,
    end: u32,
}
/// A diagnostic level
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum Level {
    /// An unrecoverable error
    Error,
    /// A warning: something the user may want to address, but which is non-fatal
    Warning,
    /// Info. unused?
    Info,
}
/// A message, associated with a location in a file.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Message {
    pub text: String,
    pub file: FileId,
    pub span: Span,
}
/// A diagnostic, including a message and additional annotations
//TODO: would this be more useful with additional annotations or a help field?
//some fancy error reporting crates have these.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Diagnostic {
    /// The main message for this diagnostic
    pub message: Message,
    /// The diagnostic level
    pub level: Level,
}
impl Span {
    /// Convert this span to a `Range<usize>`
    pub fn range(&self) -> Range<usize> {
        self.start as usize..self.end as usize
    }
}
impl Diagnostic {
    /// Create a new diagnostic
    pub fn new(
        level: Level,
        file: FileId,
        range: Range<usize>,
        message: impl Into<String>,
    ) -> Self {
        Diagnostic {
            message: Message {
                text: message.into(),
                span: Span {
                    start: range.start.try_into().unwrap(),
                    end: range.end.try_into().unwrap(),
                },
                file,
            },
            level,
        }
    }
    /// Create a new error, at the provided location
    pub fn error(file: FileId, span: Range<usize>, message: impl Into<String>) -> Self {
        Diagnostic::new(Level::Error, file, span, message)
    }
    /// Create a new warning, at the provided location
    pub fn warning(file: FileId, span: Range<usize>, message: impl Into<String>) -> Self {
        Diagnostic::new(Level::Warning, file, span, message)
    }
    /// The diagnostic's message text
    pub fn text(&self) -> &str {
        &self.message.text
    }
    /// The location of the main span, as a `Range<usize>`
    pub fn span(&self) -> Range<usize> {
        self.message.span.range()
    }
    /// `true` if this diagnostic is an error
    pub fn is_error(&self) -> bool {
        matches!(self.level, Level::Error)
    }
}