use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct Violation {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub line: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_line: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
impl Violation {
pub fn dedup_key(&self) -> String {
format!(
"{}|{}|{}|{}",
self.file.as_deref().unwrap_or(""),
self.line.map(|l| l.to_string()).unwrap_or_default(),
self.end_line.map(|l| l.to_string()).unwrap_or_default(),
self.message.as_deref().unwrap_or(""),
)
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
pub struct RuleVerdict {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relevant: Option<bool>,
#[serde(default)]
pub holds: bool,
#[serde(default)]
pub violations: Vec<Violation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rationale: Option<String>,
}
impl RuleVerdict {
pub fn is_relevant(&self) -> bool {
self.relevant != Some(false)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Outcome {
Pass,
Fail,
Skipped,
NotRelevant,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct JudgeOpinion {
#[serde(skip_serializing_if = "is_relevant")]
pub relevant: bool,
pub holds: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub rationale: Option<String>,
}
fn is_relevant(relevant: &bool) -> bool {
*relevant
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RuleOutcome {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub rationale: Option<String>,
pub outcome: Outcome,
pub votes_total: u32,
pub votes_hold: u32,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub judges: Vec<JudgeOpinion>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub violations: Vec<Violation>,
}
impl RuleOutcome {
pub fn skipped(name: impl Into<String>) -> Self {
RuleOutcome {
name: name.into(),
rationale: None,
outcome: Outcome::Skipped,
votes_total: 0,
votes_hold: 0,
judges: Vec::new(),
violations: Vec::new(),
}
}
pub fn not_relevant(name: impl Into<String>) -> Self {
RuleOutcome {
name: name.into(),
rationale: Some("declared not relevant (relevance: false)".to_string()),
outcome: Outcome::NotRelevant,
votes_total: 0,
votes_hold: 0,
judges: Vec::new(),
violations: Vec::new(),
}
}
}