use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "snake_case")]
pub enum Severity {
Hint,
Warning,
Error,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DiagnosticSource {
Rustc,
Clippy,
Fmt,
Test,
Custom,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Diagnostic {
pub severity: Severity,
pub code: Option<String>,
pub message: String,
pub file: Option<String>,
pub line: Option<u32>,
pub column: Option<u32>,
pub source: DiagnosticSource,
pub evidence: Option<String>,
}
impl Diagnostic {
pub fn new(severity: Severity, message: String, source: DiagnosticSource) -> Self {
Self {
severity,
code: None,
message,
file: None,
line: None,
column: None,
source,
evidence: None,
}
}
pub fn with_location(mut self, file: String, line: u32, column: u32) -> Self {
self.file = Some(file);
self.line = Some(line);
self.column = Some(column);
self
}
pub fn with_code(mut self, code: String) -> Self {
self.code = Some(code);
self
}
pub fn with_evidence(mut self, evidence: String) -> Self {
self.evidence = Some(evidence);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_severity_serde() {
let severities = [Severity::Hint, Severity::Warning, Severity::Error];
for sev in &severities {
let json = serde_json::to_string(sev).expect("serialize");
let deserialized: Severity = serde_json::from_str(&json).expect("deserialize");
assert_eq!(*sev, deserialized);
}
}
#[test]
fn test_severity_ordering() {
assert!(Severity::Hint < Severity::Warning);
assert!(Severity::Warning < Severity::Error);
}
#[test]
fn test_diagnostic_source_serde() {
let sources = [
DiagnosticSource::Rustc,
DiagnosticSource::Clippy,
DiagnosticSource::Fmt,
DiagnosticSource::Test,
DiagnosticSource::Custom,
];
for src in &sources {
let json = serde_json::to_string(src).expect("serialize");
let deserialized: DiagnosticSource = serde_json::from_str(&json).expect("deserialize");
assert_eq!(*src, deserialized);
}
}
#[test]
fn test_diagnostic_serde_roundtrip() {
let diag = Diagnostic::new(
Severity::Error,
"unused variable `x`".to_string(),
DiagnosticSource::Clippy,
)
.with_code("clippy::unused_variables".to_string())
.with_location("src/main.rs".to_string(), 42, 9)
.with_evidence("let x = 5;".to_string());
let json = serde_json::to_string(&diag).expect("serialize");
let deserialized: Diagnostic = serde_json::from_str(&json).expect("deserialize");
assert_eq!(diag, deserialized);
}
#[test]
fn test_diagnostic_new_defaults() {
let diag = Diagnostic::new(
Severity::Warning,
"test warning".to_string(),
DiagnosticSource::Rustc,
);
assert!(diag.code.is_none());
assert!(diag.file.is_none());
assert!(diag.line.is_none());
assert!(diag.column.is_none());
assert!(diag.evidence.is_none());
}
}