use super::{
issue_severity, issue_visible_at_severity, FsckIssue, FsckOptions, FsckReport, FsckSeverity,
};
use crate::config::ConfigSet;
#[derive(Debug, Clone, Copy)]
pub struct FsckDiagnostic<'a> {
pub issue: &'a FsckIssue,
pub severity: FsckSeverity,
pub visible: bool,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct FsckSummary<'a> {
pub diagnostics: Vec<FsckDiagnostic<'a>>,
pub ok: bool,
pub has_warnings: bool,
}
impl FsckReport {
#[must_use]
pub fn summary(
&self,
config: &ConfigSet,
options: &FsckOptions,
include_info: bool,
) -> FsckSummary<'_> {
let mut ok = true;
let mut has_warnings = false;
let diagnostics = self
.issues
.iter()
.map(|issue| {
let severity = issue_severity(issue, config, options.strict);
ok &= severity != FsckSeverity::Error;
has_warnings |= severity == FsckSeverity::Warning;
FsckDiagnostic {
issue,
severity,
visible: include_info || issue_visible_at_severity(issue, severity),
}
})
.collect();
FsckSummary {
diagnostics,
ok,
has_warnings,
}
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used, clippy::expect_used)]
use super::*;
use crate::fsck_standalone::FsckError;
use crate::objects::ObjectId;
use crate::refs_fsck::{RefsFsckIssue, RefsFsckSeverity};
fn report(ids: &[&'static str]) -> FsckReport {
FsckReport {
objects_checked: 7,
issues: ids
.iter()
.map(|id| FsckIssue::BadObject {
oid: ObjectId::zero(),
error: FsckError::new(id, "test finding"),
})
.collect(),
dangling: Vec::new(),
}
}
#[test]
fn empty_summary_is_clean() {
let report = report(&[]);
let summary = report.summary(&ConfigSet::new(), &FsckOptions::default(), false);
assert!(summary.ok);
assert!(!summary.has_warnings);
assert!(summary.diagnostics.is_empty());
}
#[test]
fn summary_retains_order_and_borrows_findings() {
let report = report(&["badFilemode", "missingTaggerEntry", "hasDot", "badTree"]);
let config = ConfigSet::new();
let options = FsckOptions::default();
let summary = report.summary(&config, &options, false);
assert!(!summary.ok);
assert!(summary.has_warnings);
assert_eq!(summary.diagnostics.len(), report.issues.len());
let classifications: Vec<_> = summary
.diagnostics
.iter()
.zip(&report.issues)
.map(|(diagnostic, issue)| {
assert!(std::ptr::eq(diagnostic.issue, issue));
(diagnostic.severity, diagnostic.visible)
})
.collect();
assert_eq!(
classifications,
vec![
(FsckSeverity::Info, false),
(FsckSeverity::Info, true),
(FsckSeverity::Warning, true),
(FsckSeverity::Error, true),
]
);
assert_eq!(summary.ok, report.ok(&config, &options));
}
#[test]
fn including_info_changes_only_visibility() {
let report = report(&["badFilemode", "missingTaggerEntry"]);
let config = ConfigSet::new();
let options = FsckOptions::full_repository(true);
for include_info in [false, true] {
let summary = report.summary(&config, &options, include_info);
assert!(summary.ok);
assert!(!summary.has_warnings);
assert_eq!(summary.diagnostics[0].visible, include_info);
assert!(summary.diagnostics[1].visible);
assert!(summary
.diagnostics
.iter()
.all(|d| d.severity == FsckSeverity::Info));
}
}
#[test]
fn summary_resolves_strictness_and_configuration() {
let mut report = report(&["hasDot", "badFilemode"]);
report.issues.push(FsckIssue::BadRef(RefsFsckIssue {
severity: RefsFsckSeverity::Warning,
path: "refs/heads/demo".into(),
msg_id: "badRefName",
detail: "test ref finding".into(),
}));
let mut config = ConfigSet::new();
let ordinary = FsckOptions::full_repository(false);
let strict = FsckOptions::full_repository(true);
let summary = report.summary(&config, &ordinary, false);
assert!(summary.ok);
assert!(summary.has_warnings);
let summary = report.summary(&config, &strict, false);
assert!(!summary.ok);
assert!(!summary.has_warnings);
assert_eq!(summary.diagnostics[0].severity, FsckSeverity::Error);
assert_eq!(summary.diagnostics[2].severity, FsckSeverity::Error);
config.add_command_override("fsck.hasDot", "warn").unwrap();
config
.add_command_override("fsck.badFilemode", "error")
.unwrap();
let summary = report.summary(&config, &strict, false);
assert!(!summary.ok);
assert!(summary.has_warnings);
assert_eq!(summary.diagnostics[0].severity, FsckSeverity::Warning);
assert_eq!(summary.diagnostics[1].severity, FsckSeverity::Error);
assert!(summary.diagnostics.iter().all(|d| d.visible));
assert_eq!(summary.ok, report.ok(&config, &strict));
}
}