use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::ops::{Add, AddAssign};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileStats {
pub blank: usize,
pub comment: usize,
pub code: usize,
}
impl FileStats {
pub fn new() -> Self {
Self::default()
}
pub fn total(&self) -> usize {
self.blank + self.comment + self.code
}
}
impl Add for FileStats {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
blank: self.blank + other.blank,
comment: self.comment + other.comment,
code: self.code + other.code,
}
}
}
impl AddAssign for FileStats {
fn add_assign(&mut self, other: Self) {
self.blank += other.blank;
self.comment += other.comment;
self.code += other.code;
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LanguageStats {
pub language: String,
pub files: usize,
pub stats: FileStats,
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct ProjectStats {
languages: HashMap<String, LanguageStats>,
}
impl ProjectStats {
pub fn new() -> Self {
Self {
languages: HashMap::new(),
}
}
pub fn add_file_stats(&mut self, language: &str, stats: FileStats) {
let lang_stats = self
.languages
.entry(language.to_string())
.or_insert_with(|| LanguageStats {
language: language.to_string(),
files: 0,
stats: FileStats::default(),
});
lang_stats.files += 1;
lang_stats.stats += stats;
}
pub fn get_languages(&self) -> Vec<&LanguageStats> {
let mut languages: Vec<_> = self.languages.values().collect();
languages.sort_by(|a, b| a.language.cmp(&b.language));
languages
}
pub fn total(&self) -> (usize, FileStats) {
let mut total_files = 0;
let mut total_stats = FileStats::default();
for lang_stats in self.languages.values() {
total_files += lang_stats.files;
total_stats += lang_stats.stats;
}
(total_files, total_stats)
}
pub fn merge(&mut self, other: ProjectStats) {
for (language, other_lang_stats) in other.languages {
let lang_stats =
self.languages
.entry(language.clone())
.or_insert_with(|| LanguageStats {
language,
files: 0,
stats: FileStats::default(),
});
lang_stats.files += other_lang_stats.files;
lang_stats.stats += other_lang_stats.stats;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_stats_new() {
let stats = FileStats::new();
assert_eq!(stats.blank, 0);
assert_eq!(stats.comment, 0);
assert_eq!(stats.code, 0);
}
#[test]
fn test_file_stats_total() {
let stats = FileStats {
blank: 10,
comment: 20,
code: 70,
};
assert_eq!(stats.total(), 100);
}
#[test]
fn test_file_stats_add() {
let stats1 = FileStats {
blank: 10,
comment: 20,
code: 30,
};
let stats2 = FileStats {
blank: 5,
comment: 15,
code: 25,
};
let result = stats1 + stats2;
assert_eq!(result.blank, 15);
assert_eq!(result.comment, 35);
assert_eq!(result.code, 55);
}
#[test]
fn test_file_stats_add_assign() {
let mut stats1 = FileStats {
blank: 10,
comment: 20,
code: 30,
};
let stats2 = FileStats {
blank: 5,
comment: 15,
code: 25,
};
stats1 += stats2;
assert_eq!(stats1.blank, 15);
assert_eq!(stats1.comment, 35);
assert_eq!(stats1.code, 55);
}
#[test]
fn test_project_stats_new() {
let stats = ProjectStats::new();
assert_eq!(stats.languages.len(), 0);
}
#[test]
fn test_project_stats_add_file_stats() {
let mut project = ProjectStats::new();
project.add_file_stats(
"Rust",
FileStats {
blank: 10,
comment: 20,
code: 70,
},
);
project.add_file_stats(
"Rust",
FileStats {
blank: 5,
comment: 10,
code: 35,
},
);
let rust_stats = &project.languages["Rust"];
assert_eq!(rust_stats.files, 2);
assert_eq!(rust_stats.stats.blank, 15);
assert_eq!(rust_stats.stats.comment, 30);
assert_eq!(rust_stats.stats.code, 105);
}
#[test]
fn test_project_stats_total() {
let mut project = ProjectStats::new();
project.add_file_stats(
"Rust",
FileStats {
blank: 10,
comment: 20,
code: 70,
},
);
project.add_file_stats(
"Python",
FileStats {
blank: 5,
comment: 10,
code: 35,
},
);
let (total_files, total_stats) = project.total();
assert_eq!(total_files, 2);
assert_eq!(total_stats.blank, 15);
assert_eq!(total_stats.comment, 30);
assert_eq!(total_stats.code, 105);
}
#[test]
fn test_project_stats_get_languages() {
let mut project = ProjectStats::new();
project.add_file_stats("Rust", FileStats::default());
project.add_file_stats("Python", FileStats::default());
project.add_file_stats("JavaScript", FileStats::default());
let languages = project.get_languages();
assert_eq!(languages.len(), 3);
assert_eq!(languages[0].language, "JavaScript");
assert_eq!(languages[1].language, "Python");
assert_eq!(languages[2].language, "Rust");
}
}