use codespan_reporting::diagnostic::Severity;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use crate::annotation::Annotation;
#[derive(
Debug, PartialEq, Eq, Ord, Copy, Clone, Hash, PartialOrd, Deserialize, Serialize, JsonSchema,
)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum IssueSeverity {
Note,
Help,
Warning,
Error,
Bug,
}
#[derive(Debug, PartialEq, Eq, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub struct Issue {
pub severity: IssueSeverity,
pub code: Option<String>,
pub message: String,
pub source: Option<(String, usize, usize)>,
pub annotations: Vec<Annotation>,
pub notes: Vec<String>,
}
impl Issue {
pub fn new<M: Into<String>>(severity: IssueSeverity, message: M) -> Self {
Self {
severity,
code: None,
message: message.into(),
source: None,
annotations: Vec::new(),
notes: Vec::new(),
}
}
pub fn error<C: Into<String>, M: Into<String>>(code: C, message: M) -> Self {
Self::new(IssueSeverity::Error, message).with_code(code)
}
pub fn warning<C: Into<String>, M: Into<String>>(code: C, message: M) -> Self {
Self::new(IssueSeverity::Warning, message).with_code(code)
}
pub fn help<C: Into<String>, M: Into<String>>(code: C, message: M) -> Self {
Self::new(IssueSeverity::Help, message).with_code(code)
}
pub fn note<C: Into<String>, M: Into<String>>(code: C, message: M) -> Self {
Self::new(IssueSeverity::Note, message).with_code(code)
}
pub fn bug<C: Into<String>, M: Into<String>>(code: C, message: M) -> Self {
Self::new(IssueSeverity::Bug, message).with_code(code)
}
pub fn from_string<M: Into<String>>(message: M) -> Self {
Self::new(IssueSeverity::Error, message)
}
#[must_use]
pub fn with_code<C: Into<String>>(mut self, code: C) -> Self {
self.code = Some(code.into());
self
}
#[must_use]
pub fn with_annotation(mut self, annotation: Annotation) -> Self {
self.annotations.push(annotation);
self
}
#[must_use]
pub fn with_note<S: Into<String>>(mut self, note: S) -> Self {
self.notes.push(note.into());
self
}
#[must_use]
pub fn with_source<O: Into<String>>(mut self, source: O, from: usize, to: usize) -> Self {
self.source = Some((source.into(), from, to));
self
}
}
#[doc(hidden)]
impl<E: std::error::Error> From<E> for Issue {
fn from(error: E) -> Self {
Issue::new(IssueSeverity::Error, error.to_string())
}
}
#[doc(hidden)]
impl From<IssueSeverity> for Severity {
fn from(severity: IssueSeverity) -> Self {
match severity {
IssueSeverity::Error => Severity::Error,
IssueSeverity::Warning => Severity::Warning,
IssueSeverity::Note => Severity::Note,
IssueSeverity::Help => Severity::Help,
IssueSeverity::Bug => Severity::Bug,
}
}
}
impl std::fmt::Display for IssueSeverity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
IssueSeverity::Error => write!(f, "error"),
IssueSeverity::Warning => write!(f, "warning"),
IssueSeverity::Help => write!(f, "help"),
IssueSeverity::Note => write!(f, "note"),
IssueSeverity::Bug => write!(f, "bug"),
}
}
}
impl std::fmt::Display for Issue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.code {
Some(code) => write!(f, "{}[{}]: {}", self.severity, code, self.message)?,
None => write!(f, "{}: {}", self.severity, self.message)?,
}
if let Some((source, from, to)) = &self.source {
write!(f, " at {source}@{from}:{to}")?;
}
Ok(())
}
}