use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Span {
pub start: usize,
pub end: usize,
}
impl Span {
pub fn clamped(start: usize, end: usize, len: usize) -> Self {
let start = start.min(len);
Self {
start,
end: end.clamp(start, len),
}
}
pub fn len(&self) -> usize {
self.end - self.start
}
pub fn is_empty(&self) -> bool {
self.start == self.end
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Severity {
Error,
Warning,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diag {
pub code: &'static str,
pub severity: Severity,
pub message: String,
pub source_name: Option<String>,
pub source_text: Option<Arc<str>>,
pub span: Option<Span>,
pub help: Option<String>,
}
impl Diag {
pub fn error(code: &'static str, message: impl Into<String>) -> Self {
Self {
code,
severity: Severity::Error,
message: message.into(),
source_name: None,
source_text: None,
span: None,
help: None,
}
}
pub fn warning(code: &'static str, message: impl Into<String>) -> Self {
Self {
severity: Severity::Warning,
..Self::error(code, message)
}
}
#[must_use]
pub fn with_source(mut self, name: impl Into<String>, text: Arc<str>) -> Self {
self.source_name = Some(name.into());
self.source_text = Some(text);
self
}
#[must_use]
pub fn with_span(mut self, span: Span) -> Self {
self.span = Some(span);
self
}
#[must_use]
pub fn with_help(mut self, help: impl Into<String>) -> Self {
self.help = Some(help.into());
self
}
}
#[derive(Debug, thiserror::Error)]
pub enum FrontError {
#[error("{} diagnostic(s)", .0.len())]
Diagnostics(Vec<Diag>),
#[error(transparent)]
Core(#[from] crate::error::CoreError),
}
impl FrontError {
pub fn exit_code(&self) -> crate::error::ExitCode {
match self {
Self::Diagnostics(_) => crate::error::ExitCode::UserError,
Self::Core(err) => err.exit_code(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spans_clamp_into_the_source() {
let span = Span::clamped(5, 12, 8);
assert_eq!((span.start, span.end), (5, 8));
let span = Span::clamped(10, 12, 8);
assert!(span.is_empty());
}
#[test]
fn front_error_maps_to_user_error() {
let err = FrontError::Diagnostics(vec![Diag::error("proef::test::x", "boom")]);
assert_eq!(err.exit_code().code(), 2);
}
}