use serde::Serialize;
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DoctorSeverity {
Error,
Warning,
Info,
}
impl DoctorSeverity {
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Error => "error",
Self::Warning => "warning",
Self::Info => "info",
}
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum DoctorFindingCode {
OrphanedWorkspace,
InvalidRepoConfig,
InvalidWorkspaceMetadata,
WorkspacePathInferred,
WorkspaceDirectoryMissing,
WorkspaceDirectoryStale,
MetadataOnlyWorkspace,
JjOnlyWorkspace,
ShellDetectionFailed,
UnsupportedShell,
HomeDirectoryMissing,
ShellRcMissing,
InvalidShellRcFile,
ShellIntegrationMissing,
}
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum DoctorScope {
Repo,
Workspace {
workspace: String,
},
Shell,
}
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
pub struct DoctorFinding {
pub severity: DoctorSeverity,
pub code: DoctorFindingCode,
pub scope: DoctorScope,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hint: Option<String>,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
pub struct DoctorSummary {
pub errors: usize,
pub warnings: usize,
pub info: usize,
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct DoctorReport {
pub findings: Vec<DoctorFinding>,
}
impl DoctorReport {
pub fn push(&mut self, finding: DoctorFinding) {
self.findings.push(finding);
}
pub fn sort(&mut self) {
self.findings.sort_by(|left, right| {
left.severity
.cmp(&right.severity)
.then_with(|| left.scope.cmp(&right.scope))
.then_with(|| left.code.cmp(&right.code))
.then_with(|| left.message.cmp(&right.message))
});
}
#[must_use]
pub fn summary(&self) -> DoctorSummary {
self.findings
.iter()
.fold(DoctorSummary::default(), |mut summary, finding| {
match finding.severity {
DoctorSeverity::Error => summary.errors += 1,
DoctorSeverity::Warning => summary.warnings += 1,
DoctorSeverity::Info => summary.info += 1,
}
summary
})
}
#[must_use]
pub fn has_errors(&self) -> bool {
self.findings
.iter()
.any(|finding| finding.severity == DoctorSeverity::Error)
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.findings.is_empty()
}
}