use crate::Applicability;
use crate::{
display::Backtrace,
location::{AsResource, AsSourceCode, AsSpan},
Location,
};
use biome_console::fmt::{self, Display};
use biome_console::{markup, MarkupBuf};
use biome_text_edit::TextEdit;
use serde::{Deserialize, Serialize};
use std::io;
pub trait Advices {
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()>;
}
pub trait Visit {
fn record_log(&mut self, category: LogCategory, text: &dyn fmt::Display) -> io::Result<()> {
let _ = (category, text);
Ok(())
}
fn record_list(&mut self, list: &[&dyn fmt::Display]) -> io::Result<()> {
let _ = list;
Ok(())
}
fn record_frame(&mut self, location: Location<'_>) -> io::Result<()> {
let _ = location;
Ok(())
}
fn record_diff(&mut self, diff: &TextEdit) -> io::Result<()> {
let _ = diff;
Ok(())
}
fn record_backtrace(
&mut self,
title: &dyn fmt::Display,
backtrace: &Backtrace,
) -> io::Result<()> {
let _ = (title, backtrace);
Ok(())
}
fn record_command(&mut self, command: &str) -> io::Result<()> {
let _ = command;
Ok(())
}
fn record_group(&mut self, title: &dyn fmt::Display, advice: &dyn Advices) -> io::Result<()> {
let _ = (title, advice);
Ok(())
}
fn record_table(
&mut self,
padding: usize,
headers: &[MarkupBuf],
columns: &[&[MarkupBuf]],
) -> io::Result<()> {
let _ = (headers, columns, padding);
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub enum LogCategory {
None,
Info,
Warn,
Error,
}
#[derive(Debug)]
pub struct LogAdvice<T> {
pub category: LogCategory,
pub text: T,
}
impl<T: Display> Advices for LogAdvice<T> {
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> {
visitor.record_log(self.category, &self.text)
}
}
#[derive(Debug)]
pub struct ListAdvice<T> {
pub list: Vec<T>,
}
impl<T: Display> Advices for ListAdvice<T> {
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> {
if self.list.is_empty() {
visitor.record_log(LogCategory::Warn, &"The list is empty.")
} else {
let pattern_list: Vec<_> = self
.list
.iter()
.map(|pattern| pattern as &dyn Display)
.collect();
visitor.record_list(&pattern_list)
}
}
}
#[derive(Debug)]
pub struct CodeFrameAdvice<Path, Span, SourceCode> {
pub path: Path,
pub span: Span,
pub source_code: SourceCode,
}
impl<Path, Span, SourceCode> Advices for CodeFrameAdvice<Path, Span, SourceCode>
where
Path: AsResource,
Span: AsSpan,
SourceCode: AsSourceCode,
{
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> {
let location = Location::builder()
.resource(&self.path)
.span(&self.span)
.source_code(&self.source_code)
.build();
visitor.record_frame(location)?;
Ok(())
}
}
#[derive(Debug)]
pub struct DiffAdvice<D> {
pub diff: D,
}
impl<D> Advices for DiffAdvice<D>
where
D: AsRef<TextEdit>,
{
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> {
visitor.record_diff(self.diff.as_ref())
}
}
#[derive(Debug)]
pub struct CommandAdvice<T> {
pub command: T,
}
impl<T> Advices for CommandAdvice<T>
where
T: AsRef<str>,
{
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> {
visitor.record_command(self.command.as_ref())
}
}
#[derive(Debug)]
pub struct CodeSuggestionAdvice<M> {
pub applicability: Applicability,
pub msg: M,
pub suggestion: TextEdit,
}
impl<M> Advices for CodeSuggestionAdvice<M>
where
M: Display,
{
fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> {
let applicability = match self.applicability {
Applicability::Always => "Safe fix",
Applicability::MaybeIncorrect => "Unsafe fix",
};
visitor.record_log(
LogCategory::Info,
&markup! {
{applicability}": "{self.msg}
},
)?;
visitor.record_diff(&self.suggestion)
}
}