use std::io;
use miette::{Diagnostic, Severity};
use serde::{Deserialize, Serialize};
use crate::diagnostics::{ShearAnalysis, ShearDiagnostic};
pub struct JsonRenderer<W> {
writer: W,
}
impl<W: io::Write> JsonRenderer<W> {
pub const fn new(writer: W) -> Self {
Self { writer }
}
pub fn render(&mut self, analysis: &ShearAnalysis) -> io::Result<()> {
let output = JsonOutput::from_analysis(analysis);
serde_json::to_writer_pretty(&mut self.writer, &output).map_err(io::Error::other)?;
writeln!(self.writer)?;
Ok(())
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct JsonOutput {
pub summary: Summary,
pub findings: Vec<Finding>,
}
impl JsonOutput {
fn from_analysis(analysis: &ShearAnalysis) -> Self {
Self {
summary: Summary {
errors: analysis.errors,
warnings: analysis.warnings,
fixed: analysis.fixed,
},
findings: analysis.findings.iter().map(Finding::from_diagnostic).collect(),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Summary {
pub errors: usize,
pub warnings: usize,
pub fixed: usize,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Finding {
pub code: String,
pub severity: String,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub location: Option<Location>,
#[serde(skip_serializing_if = "Option::is_none")]
pub help: Option<String>,
pub fixable: bool,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Location {
pub offset: usize,
pub length: usize,
}
impl Finding {
fn from_diagnostic(diagnostic: &ShearDiagnostic) -> Self {
let code = diagnostic.kind.code().to_owned();
let severity = match diagnostic.kind.severity() {
Severity::Error => "error",
Severity::Warning => "warning",
Severity::Advice => "advice",
}
.to_owned();
let message = diagnostic.kind.message();
let file = diagnostic.source.as_ref().map(|s| s.name().to_owned());
let location =
diagnostic.span.map(|span| Location { offset: span.offset(), length: span.len() });
let help = diagnostic.help().map(|h| h.to_string());
let fixable = diagnostic.kind.is_fixable();
Self { code, severity, message, file, location, help, fixable }
}
}