|
1 | | -use super::{git::metrics::GitMetrics, utils::info_field::InfoField}; |
| 1 | +use super::utils::info_field::InfoField; |
2 | 2 | use crate::{cli::NumberSeparator, info::utils::format_number}; |
| 3 | +use anyhow::Result; |
| 4 | +use gix::bstr::BString; |
| 5 | +use globset::{Glob, GlobSetBuilder}; |
3 | 6 | use serde::Serialize; |
4 | | -use std::fmt::Write; |
| 7 | +use std::{collections::HashMap, fmt::Write}; |
5 | 8 |
|
6 | 9 | #[derive(Serialize, Clone, Debug, PartialEq)] |
7 | 10 | #[serde(rename_all = "camelCase")] |
@@ -42,15 +45,62 @@ pub struct ChurnInfo { |
42 | 45 | pub file_churns: Vec<FileChurn>, |
43 | 46 | pub churn_pool_size: usize, |
44 | 47 | } |
| 48 | + |
45 | 49 | impl ChurnInfo { |
46 | | - pub fn new(git_metrics: &GitMetrics) -> Self { |
47 | | - let file_churns = git_metrics.file_churns_to_display.clone(); |
48 | | - Self { |
| 50 | + pub fn new( |
| 51 | + number_of_commits_by_file_path: &HashMap<BString, usize>, |
| 52 | + churn_pool_size: usize, |
| 53 | + number_of_file_churns_to_display: usize, |
| 54 | + globs_to_exclude: &[String], |
| 55 | + number_separator: NumberSeparator, |
| 56 | + ) -> Result<Self> { |
| 57 | + let file_churns = compute_file_churns( |
| 58 | + number_of_commits_by_file_path, |
| 59 | + number_of_file_churns_to_display, |
| 60 | + globs_to_exclude, |
| 61 | + number_separator, |
| 62 | + )?; |
| 63 | + |
| 64 | + Ok(Self { |
49 | 65 | file_churns, |
50 | | - churn_pool_size: git_metrics.churn_pool_size, |
51 | | - } |
| 66 | + churn_pool_size, |
| 67 | + }) |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +fn compute_file_churns( |
| 72 | + number_of_commits_by_file_path: &HashMap<BString, usize>, |
| 73 | + number_of_file_churns_to_display: usize, |
| 74 | + globs_to_exclude: &[String], |
| 75 | + number_separator: NumberSeparator, |
| 76 | +) -> Result<Vec<FileChurn>> { |
| 77 | + let mut builder = GlobSetBuilder::new(); |
| 78 | + for glob in globs_to_exclude { |
| 79 | + builder.add(Glob::new(glob)?); |
52 | 80 | } |
| 81 | + let glob_set = builder.build()?; |
| 82 | + let mut number_of_commits_by_file_path_sorted = Vec::from_iter(number_of_commits_by_file_path); |
| 83 | + |
| 84 | + number_of_commits_by_file_path_sorted |
| 85 | + .sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); |
| 86 | + |
| 87 | + Ok(number_of_commits_by_file_path_sorted |
| 88 | + .into_iter() |
| 89 | + .filter_map(|(file_path, nbr_of_commits)| { |
| 90 | + if !glob_set.is_match(file_path.to_string()) { |
| 91 | + Some(FileChurn::new( |
| 92 | + file_path.to_string(), |
| 93 | + *nbr_of_commits, |
| 94 | + number_separator, |
| 95 | + )) |
| 96 | + } else { |
| 97 | + None |
| 98 | + } |
| 99 | + }) |
| 100 | + .take(number_of_file_churns_to_display) |
| 101 | + .collect()) |
53 | 102 | } |
| 103 | + |
54 | 104 | impl std::fmt::Display for ChurnInfo { |
55 | 105 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { |
56 | 106 | let mut churn_info = String::new(); |
@@ -139,4 +189,35 @@ mod tests { |
139 | 189 | ); |
140 | 190 | assert_eq!(shorten_file_path("file.txt", 0), "file.txt"); |
141 | 191 | } |
| 192 | + |
| 193 | + #[test] |
| 194 | + fn test_compute_file_churns() -> Result<()> { |
| 195 | + let mut number_of_commits_by_file_path = HashMap::new(); |
| 196 | + number_of_commits_by_file_path.insert("path/to/file1.txt".into(), 2); |
| 197 | + number_of_commits_by_file_path.insert("path/to/file2.txt".into(), 5); |
| 198 | + number_of_commits_by_file_path.insert("path/to/file3.txt".into(), 3); |
| 199 | + number_of_commits_by_file_path.insert("path/to/file4.txt".into(), 7); |
| 200 | + number_of_commits_by_file_path.insert("foo/x/y/file.txt".into(), 70); |
| 201 | + number_of_commits_by_file_path.insert("foo/x/file.txt".into(), 10); |
| 202 | + |
| 203 | + let number_of_file_churns_to_display = 3; |
| 204 | + let number_separator = NumberSeparator::Comma; |
| 205 | + let globs_to_exclude = vec![ |
| 206 | + "foo/**/file.txt".to_string(), |
| 207 | + "path/to/file2.txt".to_string(), |
| 208 | + ]; |
| 209 | + let actual = compute_file_churns( |
| 210 | + &number_of_commits_by_file_path, |
| 211 | + number_of_file_churns_to_display, |
| 212 | + &globs_to_exclude, |
| 213 | + number_separator, |
| 214 | + )?; |
| 215 | + let expected = vec![ |
| 216 | + FileChurn::new(String::from("path/to/file4.txt"), 7, number_separator), |
| 217 | + FileChurn::new(String::from("path/to/file3.txt"), 3, number_separator), |
| 218 | + FileChurn::new(String::from("path/to/file1.txt"), 2, number_separator), |
| 219 | + ]; |
| 220 | + assert_eq!(actual, expected); |
| 221 | + Ok(()) |
| 222 | + } |
142 | 223 | } |
0 commit comments