use crate::changes::Change;
use core::num::NonZeroU32;
use rust_llm_tidy_lint::{Diagnostic, Severity};
use serde::Serialize;
use std::borrow::Cow;
use std::io::Write;
use std::path::{Path, PathBuf};
#[derive(Serialize)]
pub(crate) struct JsonRecord<'a> {
path: Cow<'a, str>,
line: Option<NonZeroU32>,
severity: &'static str,
code: &'static str,
message: Cow<'a, str>,
item_kind: Cow<'a, str>,
item_name: Option<Cow<'a, str>>,
title: Option<&'static str>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub(crate) enum OutputMode {
Text,
Json,
}
pub(crate) fn emit_json(
diagnostics: &[(PathBuf, Diagnostic)],
changes: &[(PathBuf, Change)],
) -> anyhow::Result<()> {
let mut records: Vec<JsonRecord<'_>> = Vec::with_capacity(diagnostics.len() + changes.len());
records.extend(diagnostics.iter().map(|(path, d)| project_lint(path, d)));
records.extend(changes.iter().map(|(path, c)| project_change(path, c)));
let doc = serde_json::to_string(&records)?;
let mut out = std::io::stdout().lock();
out.write_all(doc.as_bytes())?;
out.write_all(b"\n")?;
Ok(())
}
fn project_change<'a>(path: &Path, c: &'a Change) -> JsonRecord<'a> {
JsonRecord {
path: Cow::Owned(path.display().to_string()),
line: c.line,
severity: "success",
code: c.code,
message: Cow::Borrowed(c.message.as_ref()),
item_kind: Cow::Borrowed(c.kind.as_str()),
item_name: c.name.as_deref().map(Cow::Borrowed),
title: None,
}
}
fn project_lint<'a>(path: &Path, d: &'a Diagnostic) -> JsonRecord<'a> {
JsonRecord {
path: Cow::Owned(path.display().to_string()),
line: NonZeroU32::new(d.line as u32),
severity: match d.severity {
Severity::Error => "error",
Severity::Warning => "warning",
Severity::Hint => "hint",
},
code: d.code,
message: Cow::Borrowed(d.message.as_ref()),
item_kind: Cow::Borrowed(d.item_kind.as_ref()),
item_name: d.item_name.as_deref().map(Cow::Borrowed),
title: Some(d.title()),
}
}
#[cfg(test)]
mod tests {
use super::project_lint;
use rust_llm_tidy_lint::{Diagnostic, Severity};
use std::path::Path;
#[test]
fn project_lint_serializes_hint_severity() {
let finding = Diagnostic {
severity: Severity::Hint,
code: "DOC999",
message: String::from("consider pre-allocating the buffer"),
line: 3,
item_kind: String::from("fn"),
item_name: Some(String::from("load")),
};
let json = serde_json::to_string(&project_lint(Path::new("src/lib.rs"), &finding)).unwrap();
assert_eq!(
json,
"{\"path\":\"src/lib.rs\",\"line\":3,\"severity\":\"hint\",\
\"code\":\"DOC999\",\"message\":\"consider pre-allocating the buffer\",\
\"item_kind\":\"fn\",\"item_name\":\"load\",\"title\":\"DOC999\"}"
);
}
}