1use std::fmt;
2use std::sync::Arc;
3
4pub use crate::checks::{ViolationType, VisibilityDetail, lookup_rationale};
5
6#[derive(Debug, Clone, Copy, serde::Serialize)]
8pub struct CheckRationale {
9 pub problem: &'static str,
11 pub fix: &'static str,
13 pub exception: &'static str,
15 pub llm_specific: bool,
17}
18
19impl fmt::Display for CheckRationale {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 writeln!(f, "Problem: {}", self.problem)?;
22 writeln!(f, "Fix: {}", self.fix)?;
23 writeln!(f, "Exception: {}", self.exception)?;
24 write!(f, "LLM-specific: {}", self.llm_specific)
25 }
26}
27
28impl ViolationType {
29 pub fn pattern(&self) -> Option<&str> {
31 match self {
32 Self::ForbiddenAttribute { pattern }
33 | Self::ForbiddenType { pattern }
34 | Self::ForbiddenCall { pattern }
35 | Self::ForbiddenMacro { pattern } => Some(pattern),
36 _ => None,
37 }
38 }
39
40 pub fn visibility_detail(&self) -> Option<&VisibilityDetail> {
42 match self {
43 Self::ItemVisibilityPolicy { detail } => Some(detail),
44 _ => None,
45 }
46 }
47}
48
49impl fmt::Display for ViolationType {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 write!(f, "{}", self.code())
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize)]
61#[serde(rename_all = "lowercase")]
62pub enum Severity {
63 Warn,
65 #[default]
67 Deny,
68}
69
70impl Severity {
71 pub fn is_deny(self) -> bool {
73 matches!(self, Self::Deny)
74 }
75
76 pub fn as_str(self) -> &'static str {
78 match self {
79 Self::Warn => "warn",
80 Self::Deny => "deny",
81 }
82 }
83}
84
85impl fmt::Display for Severity {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 f.write_str(self.as_str())
88 }
89}
90
91#[derive(Debug, Clone)]
93pub struct Violation {
94 pub violation_type: ViolationType,
96 pub file_path: Arc<str>,
98 pub line: usize,
100 pub column: usize,
102 pub message: Box<str>,
104 pub severity: Severity,
106}
107
108impl Violation {
109 pub fn new(
111 violation_type: ViolationType,
112 file_path: Arc<str>,
113 line: usize,
114 column: usize,
115 message: impl Into<Box<str>>,
116 ) -> Self {
117 Self {
118 violation_type,
119 file_path,
120 line,
121 column,
122 message: message.into(),
123 severity: Severity::Deny,
124 }
125 }
126
127 pub fn with_severity(mut self, severity: Severity) -> Self {
129 self.severity = severity;
130 self
131 }
132
133 pub fn rationale(&self) -> CheckRationale {
135 self.violation_type.rationale()
136 }
137}
138
139impl fmt::Display for Violation {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write!(
142 f,
143 "{}:{}:{}: {}: {}",
144 self.file_path, self.line, self.column, self.violation_type, self.message
145 )
146 }
147}