use std::fmt::{self, Display, Formatter};
use miette::{
Diagnostic, LabeledSpan, MietteError, MietteSpanContents, Severity, SourceCode, SourceSpan,
SpanContents,
};
#[derive(Clone, Debug, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[allow(missing_docs)]
pub enum Level {
Info,
Warning,
Error,
}
#[derive(Default, PartialEq, Clone, Eq, Debug)]
pub struct Location {
pub file: Option<String>,
pub package: Option<String>,
pub entity: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("{message}")]
pub struct Message {
pub message: String,
pub help: String,
}
impl Diagnostic for Message {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Violation {
pub rule: String,
pub level: Level,
pub message: Message,
pub location: Location,
pub info: String,
}
pub type Violations = Vec<Violation>;
impl std::error::Error for Violation {}
impl Display for Violation {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", &self.info)
}
}
impl Diagnostic for Violation {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
Some(Box::new(self.rule.split("::").last().unwrap_or(&self.rule)))
}
fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
Some(Box::new(
"https://helsing-ai.github.io/buffrs/reference/protocol-buffer-rules.html",
))
}
fn severity(&self) -> Option<Severity> {
let level = match self.level {
Level::Info => Severity::Advice,
Level::Warning => Severity::Warning,
Level::Error => Severity::Error,
};
Some(level)
}
fn source_code(&self) -> Option<&dyn SourceCode> {
Some(&self.location)
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
Some(Box::new(
[LabeledSpan::new(Some("file".into()), 0, 0)].into_iter(),
))
}
fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
Some(&self.message)
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
Some(Box::new(&self.message.help))
}
}
impl SourceCode for Location {
fn read_span<'a>(
&'a self,
span: &SourceSpan,
_context_lines_before: usize,
_context_lines_after: usize,
) -> Result<Box<dyn SpanContents<'a> + 'a>, MietteError> {
Ok(Box::new(MietteSpanContents::new_named(
self.file.clone().unwrap_or_default(),
&[],
*span,
0,
0,
0,
)))
}
}