impl TdgBaseline {
pub fn new(git_context: Option<GitContext>) -> Self {
Self {
version: env!("CARGO_PKG_VERSION").to_string(),
created_at: Utc::now(),
git_context,
files: HashMap::new(),
summary: BaselineSummary {
total_files: 0,
avg_score: 0.0,
grade_distribution: HashMap::new(),
languages: HashMap::new(),
},
}
}
pub fn add_entry(&mut self, path: PathBuf, entry: BaselineEntry) {
self.files.insert(path, entry);
self.recompute_summary();
}
fn recompute_summary(&mut self) {
self.summary.total_files = self.files.len();
if self.files.is_empty() {
self.summary.avg_score = 0.0;
return;
}
let total: f32 = self.files.values().map(|e| e.score.total).sum();
self.summary.avg_score = total / self.files.len() as f32;
self.summary.grade_distribution.clear();
for entry in self.files.values() {
*self
.summary
.grade_distribution
.entry(entry.score.grade)
.or_insert(0) += 1;
}
self.summary.languages.clear();
for entry in self.files.values() {
let lang = format!("{:?}", entry.score.language);
*self.summary.languages.entry(lang).or_insert(0) += 1;
}
}
pub fn compare(&self, other: &TdgBaseline) -> BaselineComparison {
let mut improved = Vec::new();
let mut regressed = Vec::new();
let mut unchanged = Vec::new();
let mut added = Vec::new();
let mut removed = Vec::new();
for (path, new_entry) in &other.files {
if let Some(old_entry) = self.files.get(path) {
let delta = new_entry.score.total - old_entry.score.total;
if delta.abs() < 0.01 {
unchanged.push(path.clone());
} else if delta > 0.0 {
improved.push(FileComparison {
path: path.clone(),
old_score: old_entry.score.clone(),
new_score: new_entry.score.clone(),
delta,
grade_change: (old_entry.score.grade, new_entry.score.grade),
});
} else {
regressed.push(FileComparison {
path: path.clone(),
old_score: old_entry.score.clone(),
new_score: new_entry.score.clone(),
delta,
grade_change: (old_entry.score.grade, new_entry.score.grade),
});
}
} else {
added.push(path.clone());
}
}
for path in self.files.keys() {
if !other.files.contains_key(path) {
removed.push(path.clone());
}
}
improved.sort_by(|a, b| b.delta.total_cmp(&a.delta));
regressed.sort_by(|a, b| a.delta.total_cmp(&b.delta));
BaselineComparison {
improved,
regressed,
unchanged,
added,
removed,
}
}
pub fn save(&self, path: impl AsRef<Path>) -> Result<()> {
let json = serde_json::to_string_pretty(self)?;
std::fs::write(path, json)?;
Ok(())
}
pub fn load(path: impl AsRef<Path>) -> Result<Self> {
let json = std::fs::read_to_string(path)?;
let baseline = serde_json::from_str(&json)?;
Ok(baseline)
}
}