use crate::query::options::LineTypes;
use serde::{Deserialize, Serialize};
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::path::PathBuf;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct Locs {
pub code: u64,
pub tests: u64,
pub examples: u64,
pub docs: u64,
pub comments: u64,
pub blanks: u64,
pub total: u64,
}
impl Locs {
pub fn new() -> Self {
Self::default()
}
pub fn total(&self) -> u64 {
self.total
}
pub fn total_logic(&self) -> u64 {
self.code + self.tests + self.examples
}
pub fn recompute_total(&mut self) {
self.total =
self.code + self.tests + self.examples + self.docs + self.comments + self.blanks;
}
pub fn filter(&self, types: LineTypes) -> Self {
Self {
code: if types.code { self.code } else { 0 },
tests: if types.tests { self.tests } else { 0 },
examples: if types.examples { self.examples } else { 0 },
docs: if types.docs { self.docs } else { 0 },
comments: if types.comments { self.comments } else { 0 },
blanks: if types.blanks { self.blanks } else { 0 },
total: self.total, }
}
}
impl Add for Locs {
type Output = Self;
fn add(self, other: Self) -> Self {
Self {
code: self.code + other.code,
tests: self.tests + other.tests,
examples: self.examples + other.examples,
docs: self.docs + other.docs,
comments: self.comments + other.comments,
blanks: self.blanks + other.blanks,
total: self.total + other.total,
}
}
}
impl AddAssign for Locs {
fn add_assign(&mut self, other: Self) {
self.code += other.code;
self.tests += other.tests;
self.examples += other.examples;
self.docs += other.docs;
self.comments += other.comments;
self.blanks += other.blanks;
self.total += other.total;
}
}
impl Sub for Locs {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self {
code: self.code.saturating_sub(other.code),
tests: self.tests.saturating_sub(other.tests),
examples: self.examples.saturating_sub(other.examples),
docs: self.docs.saturating_sub(other.docs),
comments: self.comments.saturating_sub(other.comments),
blanks: self.blanks.saturating_sub(other.blanks),
total: self.total.saturating_sub(other.total),
}
}
}
impl SubAssign for Locs {
fn sub_assign(&mut self, other: Self) {
self.code = self.code.saturating_sub(other.code);
self.tests = self.tests.saturating_sub(other.tests);
self.examples = self.examples.saturating_sub(other.examples);
self.docs = self.docs.saturating_sub(other.docs);
self.comments = self.comments.saturating_sub(other.comments);
self.blanks = self.blanks.saturating_sub(other.blanks);
self.total = self.total.saturating_sub(other.total);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileStats {
pub path: PathBuf,
pub stats: Locs,
}
impl FileStats {
pub fn new(path: PathBuf, stats: Locs) -> Self {
Self { path, stats }
}
pub fn filter(&self, types: LineTypes) -> Self {
Self {
path: self.path.clone(),
stats: self.stats.filter(types),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ModuleStats {
pub name: String,
pub stats: Locs,
pub files: Vec<PathBuf>,
}
impl ModuleStats {
pub fn new(name: String) -> Self {
Self {
name,
stats: Locs::new(),
files: Vec::new(),
}
}
pub fn add_file(&mut self, path: PathBuf, stats: Locs) {
self.stats += stats;
self.files.push(path);
}
pub fn filter(&self, types: LineTypes) -> Self {
Self {
name: self.name.clone(),
stats: self.stats.filter(types),
files: self.files.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CrateStats {
pub name: String,
pub path: PathBuf,
pub stats: Locs,
pub files: Vec<FileStats>,
}
impl CrateStats {
pub fn new(name: String, path: PathBuf) -> Self {
Self {
name,
path,
stats: Locs::new(),
files: Vec::new(),
}
}
pub fn add_file(&mut self, file_stats: FileStats) {
self.stats += file_stats.stats;
self.files.push(file_stats);
}
pub fn filter(&self, types: LineTypes) -> Self {
Self {
name: self.name.clone(),
path: self.path.clone(),
stats: self.stats.filter(types),
files: self.files.iter().map(|f| f.filter(types)).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_locs_default() {
let locs = Locs::new();
assert_eq!(locs.code, 0);
assert_eq!(locs.tests, 0);
assert_eq!(locs.examples, 0);
assert_eq!(locs.docs, 0);
assert_eq!(locs.comments, 0);
assert_eq!(locs.blanks, 0);
assert_eq!(locs.total, 0);
assert_eq!(locs.total(), 0);
}
#[test]
fn test_locs_total() {
let locs = Locs {
code: 100,
tests: 50,
examples: 20,
docs: 30,
comments: 10,
blanks: 15,
total: 225,
};
assert_eq!(locs.total(), 225);
assert_eq!(locs.total_logic(), 170);
}
#[test]
fn test_locs_add() {
let a = Locs {
code: 100,
tests: 50,
examples: 20,
docs: 30,
comments: 10,
blanks: 15,
total: 225,
};
let b = Locs {
code: 50,
tests: 25,
examples: 10,
docs: 15,
comments: 5,
blanks: 10,
total: 115,
};
let sum = a + b;
assert_eq!(sum.code, 150);
assert_eq!(sum.tests, 75);
assert_eq!(sum.examples, 30);
assert_eq!(sum.docs, 45);
assert_eq!(sum.comments, 15);
assert_eq!(sum.blanks, 25);
assert_eq!(sum.total, 340);
}
#[test]
fn test_locs_filter() {
let locs = Locs {
code: 100,
tests: 50,
examples: 20,
docs: 30,
comments: 10,
blanks: 15,
total: 225,
};
let code_only = locs.filter(LineTypes::new().with_code());
assert_eq!(code_only.code, 100);
assert_eq!(code_only.tests, 0);
assert_eq!(code_only.examples, 0);
assert_eq!(code_only.docs, 0);
assert_eq!(code_only.comments, 0);
assert_eq!(code_only.blanks, 0);
assert_eq!(code_only.total, 225);
let code_tests = locs.filter(LineTypes::new().with_code().with_tests());
assert_eq!(code_tests.code, 100);
assert_eq!(code_tests.tests, 50);
assert_eq!(code_tests.examples, 0);
assert_eq!(code_tests.total, 225); }
#[test]
fn test_recompute_total() {
let mut locs = Locs {
code: 100,
tests: 50,
examples: 20,
docs: 30,
comments: 10,
blanks: 15,
total: 0, };
locs.recompute_total();
assert_eq!(locs.total, 225);
}
}