use zuit_core::analyzer::Severity;
use zuit_core::engine::Report;
use zuit_core::finding::Finding;
use quick_xml::Writer;
use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, Event};
use crate::ReportError;
fn severity_to_checkstyle(severity: Severity) -> &'static str {
match severity {
Severity::Critical | Severity::High => "error",
Severity::Medium => "warning",
Severity::Low | Severity::Info => "info",
}
}
fn sort_key(f: &Finding) -> (String, u32, u32, &str) {
(
f.location.file.to_string_lossy().into_owned(),
f.location.start.line,
f.location.start.column,
f.rule_id.as_str(),
)
}
pub fn render_checkstyle(report: &Report) -> Result<String, ReportError> {
let mut sorted: Vec<&Finding> = report.findings.iter().collect();
sorted.sort_by_key(|f| sort_key(f));
let mut groups: Vec<(String, Vec<&Finding>)> = Vec::new();
for finding in &sorted {
let file = finding.location.file.to_string_lossy().into_owned();
let same_file = groups.last().is_some_and(|(k, _)| k == &file);
if same_file {
if let Some(last) = groups.last_mut() {
last.1.push(finding);
}
} else {
groups.push((file, vec![finding]));
}
}
let mut buf: Vec<u8> = Vec::new();
let mut writer = Writer::new(&mut buf);
writer
.write_event(Event::Decl(BytesDecl::new("1.0", Some("UTF-8"), None)))
.map_err(ReportError::Xml)?;
let mut checkstyle_elem = BytesStart::new("checkstyle");
checkstyle_elem.push_attribute(("version", "10.0"));
writer
.write_event(Event::Start(checkstyle_elem))
.map_err(ReportError::Xml)?;
for (file_name, findings) in &groups {
let mut file_elem = BytesStart::new("file");
file_elem.push_attribute(("name", file_name.as_str()));
writer
.write_event(Event::Start(file_elem))
.map_err(ReportError::Xml)?;
for finding in findings {
let line = finding.location.start.line.to_string();
let column = finding.location.start.column.to_string();
let severity = severity_to_checkstyle(finding.severity);
let source = format!("zuit.{}", finding.rule_id);
let mut error_elem = BytesStart::new("error");
error_elem.push_attribute(("line", line.as_str()));
error_elem.push_attribute(("column", column.as_str()));
error_elem.push_attribute(("severity", severity));
error_elem.push_attribute(("message", finding.message.as_str()));
error_elem.push_attribute(("source", source.as_str()));
writer
.write_event(Event::Empty(error_elem))
.map_err(ReportError::Xml)?;
}
writer
.write_event(Event::End(BytesEnd::new("file")))
.map_err(ReportError::Xml)?;
}
writer
.write_event(Event::End(BytesEnd::new("checkstyle")))
.map_err(ReportError::Xml)?;
String::from_utf8(buf).map_err(|e| ReportError::Xml(quick_xml::Error::from(e)))
}