use std::{
convert::Infallible,
fmt::{Debug, Display},
io,
ops::{BitOr, BitOrAssign},
str::FromStr,
};
use enumflags2::{bitflags, make_bitflags, BitFlags};
use serde::{Deserialize, Serialize};
use biome_console::fmt;
use crate::{Category, Location, Visit};
pub trait Diagnostic: Debug {
fn category(&self) -> Option<&'static Category> {
None
}
fn severity(&self) -> Severity {
Severity::Error
}
fn description(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let _ = fmt;
Ok(())
}
fn message(&self, fmt: &mut fmt::Formatter<'_>) -> io::Result<()> {
let _ = fmt;
Ok(())
}
fn advices(&self, visitor: &mut dyn Visit) -> io::Result<()> {
let _ = visitor;
Ok(())
}
fn verbose_advices(&self, visitor: &mut dyn Visit) -> io::Result<()> {
let _ = visitor;
Ok(())
}
fn location(&self) -> Location<'_> {
Location::builder().build()
}
fn tags(&self) -> DiagnosticTags {
DiagnosticTags::empty()
}
fn source(&self) -> Option<&dyn Diagnostic> {
None
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub enum Severity {
Hint,
#[default]
Information,
Warning,
Error,
Fatal,
}
impl FromStr for Severity {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"hint" => Ok(Self::Information),
"info" => Ok(Self::Information),
"warn" => Ok(Self::Warning),
"error" => Ok(Self::Error),
v => Err(format!(
"Found unexpected value ({v}), valid values are: info, warn, error."
)),
}
}
}
impl Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Hint => write!(f, "info"),
Self::Information => write!(f, "info"),
Self::Warning => write!(f, "warn"),
Self::Error => write!(f, "error"),
Self::Fatal => write!(f, "fatal"),
}
}
}
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
#[bitflags]
#[repr(u8)]
pub(super) enum DiagnosticTag {
Fixable = 1 << 0,
Internal = 1 << 1,
UnnecessaryCode = 1 << 2,
DeprecatedCode = 1 << 3,
Verbose = 1 << 4,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct DiagnosticTags(BitFlags<DiagnosticTag>);
impl DiagnosticTags {
pub const FIXABLE: Self = Self(make_bitflags!(DiagnosticTag::{Fixable}));
pub const INTERNAL: Self = Self(make_bitflags!(DiagnosticTag::{Internal}));
pub const UNNECESSARY_CODE: Self = Self(make_bitflags!(DiagnosticTag::{UnnecessaryCode}));
pub const DEPRECATED_CODE: Self = Self(make_bitflags!(DiagnosticTag::{DeprecatedCode}));
pub const VERBOSE: Self = Self(make_bitflags!(DiagnosticTag::{Verbose}));
pub const fn all() -> Self {
Self(BitFlags::ALL)
}
pub const fn empty() -> Self {
Self(BitFlags::EMPTY)
}
pub fn insert(&mut self, other: DiagnosticTags) {
self.0 |= other.0;
}
pub fn contains(self, other: impl Into<DiagnosticTags>) -> bool {
self.0.contains(other.into().0)
}
pub const fn union(self, other: Self) -> Self {
Self(self.0.union_c(other.0))
}
pub fn is_empty(self) -> bool {
self.0.is_empty()
}
pub fn is_verbose(&self) -> bool {
self.contains(DiagnosticTag::Verbose)
}
}
impl BitOr for DiagnosticTags {
type Output = Self;
fn bitor(self, rhs: Self) -> Self::Output {
DiagnosticTags(self.0 | rhs.0)
}
}
impl BitOrAssign for DiagnosticTags {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl Diagnostic for Infallible {}
pub(crate) mod internal {
use std::fmt::Debug;
use crate::Diagnostic;
pub trait AsDiagnostic: Debug {
type Diagnostic: Diagnostic + ?Sized;
fn as_diagnostic(&self) -> &Self::Diagnostic;
fn as_dyn(&self) -> &dyn Diagnostic;
}
impl<D: Diagnostic> AsDiagnostic for D {
type Diagnostic = D;
fn as_diagnostic(&self) -> &Self::Diagnostic {
self
}
fn as_dyn(&self) -> &dyn Diagnostic {
self
}
}
}