ralph/commands/doctor/
types.rs1use crate::contracts::BlockingState;
17use serde::Serialize;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
21#[serde(rename_all = "PascalCase")]
22pub enum CheckSeverity {
23 Success,
25 Warning,
27 Error,
29}
30
31#[derive(Debug, Clone, Serialize)]
33pub struct CheckResult {
34 pub category: String,
36 pub check: String,
38 pub severity: CheckSeverity,
40 pub message: String,
42 pub fix_available: bool,
44 #[serde(skip_serializing_if = "Option::is_none")]
46 pub fix_applied: Option<bool>,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub suggested_fix: Option<String>,
50 #[serde(skip_serializing_if = "Option::is_none")]
52 pub blocking: Option<BlockingState>,
53}
54
55impl CheckResult {
56 pub fn success(category: &str, check: &str, message: &str) -> Self {
58 Self {
59 category: category.to_string(),
60 check: check.to_string(),
61 severity: CheckSeverity::Success,
62 message: message.to_string(),
63 fix_available: false,
64 fix_applied: None,
65 suggested_fix: None,
66 blocking: None,
67 }
68 }
69
70 pub fn warning(
72 category: &str,
73 check: &str,
74 message: &str,
75 fix_available: bool,
76 suggested_fix: Option<&str>,
77 ) -> Self {
78 Self {
79 category: category.to_string(),
80 check: check.to_string(),
81 severity: CheckSeverity::Warning,
82 message: message.to_string(),
83 fix_available,
84 fix_applied: None,
85 suggested_fix: suggested_fix.map(|s| s.to_string()),
86 blocking: None,
87 }
88 }
89
90 pub fn error(
92 category: &str,
93 check: &str,
94 message: &str,
95 fix_available: bool,
96 suggested_fix: Option<&str>,
97 ) -> Self {
98 Self {
99 category: category.to_string(),
100 check: check.to_string(),
101 severity: CheckSeverity::Error,
102 message: message.to_string(),
103 fix_available,
104 fix_applied: None,
105 suggested_fix: suggested_fix.map(|s| s.to_string()),
106 blocking: None,
107 }
108 }
109
110 pub fn with_fix_applied(mut self, applied: bool) -> Self {
112 self.fix_applied = Some(applied);
113 self
114 }
115
116 pub fn with_blocking(mut self, blocking: BlockingState) -> Self {
118 self.blocking = Some(blocking);
119 self
120 }
121}
122
123#[derive(Debug, Clone, Serialize)]
125pub struct Summary {
126 pub total: usize,
128 pub passed: usize,
130 pub warnings: usize,
132 pub errors: usize,
134 pub fixes_applied: usize,
136 pub fixes_failed: usize,
138}
139
140#[derive(Debug, Clone, Serialize)]
142pub struct DoctorReport {
143 pub success: bool,
145 #[serde(skip_serializing_if = "Option::is_none")]
147 pub blocking: Option<BlockingState>,
148 pub checks: Vec<CheckResult>,
150 pub summary: Summary,
152}
153
154impl DoctorReport {
155 pub fn new() -> Self {
157 Self {
158 success: true,
159 blocking: None,
160 checks: Vec::new(),
161 summary: Summary {
162 total: 0,
163 passed: 0,
164 warnings: 0,
165 errors: 0,
166 fixes_applied: 0,
167 fixes_failed: 0,
168 },
169 }
170 }
171
172 pub fn add(&mut self, result: CheckResult) {
174 self.summary.total += 1;
175 match result.severity {
176 CheckSeverity::Success => self.summary.passed += 1,
177 CheckSeverity::Warning => self.summary.warnings += 1,
178 CheckSeverity::Error => {
179 self.summary.errors += 1;
180 self.success = false;
181 }
182 }
183 if result.fix_applied == Some(true) {
184 self.summary.fixes_applied += 1;
185 } else if result.fix_applied == Some(false) {
186 self.summary.fixes_failed += 1;
187 }
188 self.checks.push(result);
189 }
190}
191
192impl Default for DoctorReport {
193 fn default() -> Self {
194 Self::new()
195 }
196}