From c6908e47e207d33e8cab10afb7c795e900100252 Mon Sep 17 00:00:00 2001 From: o2sh Date: Sat, 3 Jul 2021 04:13:31 +0200 Subject: [PATCH 1/2] display number of contributors --- src/info/author.rs | 43 +++++++++++++++++++++++++++++++++++ src/info/info_field.rs | 15 ++++++++----- src/info/mod.rs | 51 ++++++++++++++++++------------------------ src/info/repo.rs | 44 ++++++++++++++++++------------------ 4 files changed, 96 insertions(+), 57 deletions(-) create mode 100644 src/info/author.rs diff --git a/src/info/author.rs b/src/info/author.rs new file mode 100644 index 000000000..1661e7516 --- /dev/null +++ b/src/info/author.rs @@ -0,0 +1,43 @@ +use serde::ser::SerializeStruct; +use serde::Serialize; + +pub struct Author { + name: String, + email: Option, + nbr_of_commits: usize, + contribution: usize, +} + +impl Author { + pub fn new( + name: String, + email: Option, + nbr_of_commits: usize, + total_nbr_of_commits: usize, + ) -> Self { + let contribution = + (nbr_of_commits as f32 * 100. / total_nbr_of_commits as f32).round() as usize; + Self { name, email, nbr_of_commits, contribution } + } +} + +impl std::fmt::Display for Author { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if let Some(email) = &self.email { + write!(f, "{}% {} <{}> {}", self.contribution, self.name, email, self.nbr_of_commits) + } else { + write!(f, "{}% {} {}", self.contribution, self.name, self.nbr_of_commits) + } + } +} + +impl Serialize for Author { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Author", 1)?; + state.serialize_field("name", &self.name)?; + state.end() + } +} diff --git a/src/info/info_field.rs b/src/info/info_field.rs index 8d8c83a67..510dc2adc 100644 --- a/src/info/info_field.rs +++ b/src/info/info_field.rs @@ -8,15 +8,16 @@ pub enum InfoField { GitInfo, Project, HEAD, + Pending, Version, Created, - Dependencies, Languages, + Dependencies, Authors, LastChange, + Contributors, Repo, Commits, - Pending, LinesOfCode, Size, License, @@ -27,15 +28,16 @@ pub struct InfoFieldOff { pub git_info: bool, pub project: bool, pub head: bool, + pub pending: bool, pub version: bool, pub created: bool, - pub dependencies: bool, pub languages: bool, + pub dependencies: bool, pub authors: bool, pub last_change: bool, + pub contributors: bool, pub repo: bool, pub commits: bool, - pub pending: bool, pub lines_of_code: bool, pub size: bool, pub license: bool, @@ -52,14 +54,15 @@ impl InfoFieldOff { InfoField::GitInfo => info_field_off.git_info = true, InfoField::Project => info_field_off.project = true, InfoField::HEAD => info_field_off.head = true, + InfoField::Pending => info_field_off.pending = true, InfoField::Version => info_field_off.version = true, InfoField::Created => info_field_off.created = true, - InfoField::Dependencies => info_field_off.dependencies = true, InfoField::Languages => info_field_off.languages = true, + InfoField::Dependencies => info_field_off.dependencies = true, InfoField::Authors => info_field_off.authors = true, InfoField::LastChange => info_field_off.last_change = true, + InfoField::Contributors => info_field_off.contributors = true, InfoField::Repo => info_field_off.repo = true, - InfoField::Pending => info_field_off.pending = true, InfoField::Commits => info_field_off.commits = true, InfoField::LinesOfCode => info_field_off.lines_of_code = true, InfoField::Size => info_field_off.size = true, diff --git a/src/info/mod.rs b/src/info/mod.rs index 481b50286..1f3cf8d49 100644 --- a/src/info/mod.rs +++ b/src/info/mod.rs @@ -2,6 +2,7 @@ use crate::cli::{self, Config}; use crate::error::*; use crate::ui::get_ascii_colors; use crate::ui::text_color::TextColor; +use author::Author; use colored::{Color, ColoredString, Colorize}; use deps::DependencyDetector; use git2::Repository; @@ -12,6 +13,7 @@ use repo::Repo; use serde::ser::SerializeStruct; use serde::Serialize; +mod author; pub mod deps; mod head_refs; pub mod info_field; @@ -31,8 +33,9 @@ pub struct Info { creation_date: String, languages: Vec<(Language, f64)>, dependencies: String, - authors: Vec<(String, Option, usize, usize)>, + authors: Vec, last_change: String, + contributors: usize, repo_url: String, number_of_commits: String, lines_of_code: usize, @@ -151,6 +154,17 @@ impl std::fmt::Display for Info { )?; } + if !self.config.disabled_fields.contributors + && self.contributors > self.config.number_of_authors + { + writeln!( + f, + "{}{}", + &self.get_formatted_subtitle_label("Contributors"), + &self.contributors.to_string().color(self.text_colors.info), + )?; + } + if !self.config.disabled_fields.repo && !self.repo_url.is_empty() { writeln!( f, @@ -231,7 +245,8 @@ impl Info { let number_of_branches = internal_repo.get_number_of_branches()?; let creation_date = internal_repo.get_creation_date(config.iso_time)?; let number_of_commits = internal_repo.get_number_of_commits(); - let authors = internal_repo.get_authors(config.number_of_authors, config.show_email)?; + let (authors, contributors) = + internal_repo.get_authors(config.number_of_authors, config.show_email)?; let last_change = internal_repo.get_date_of_last_commit(config.iso_time); let (repo_size, file_count) = internal_repo.get_repo_size(); let workdir = internal_repo.get_work_dir()?; @@ -262,6 +277,7 @@ impl Info { dependencies, authors, last_change, + contributors, repo_url, number_of_commits, lines_of_code, @@ -322,33 +338,11 @@ impl Info { let pad = title.len() + 2; - for (i, (author_name, author_email_opt, author_nbr_commits, autor_contribution)) in - self.authors.iter().enumerate() - { - let author = if let Some(author_email) = author_email_opt { - format!("{} <{}>", author_name, author_email) - } else { - author_name.to_owned() - }; - + for (i, author) in self.authors.iter().enumerate() { if i == 0 { - author_field.push_str(&format!( - "{}{} {} {}\n", - autor_contribution.to_string().color(self.text_colors.info), - "%".color(self.text_colors.info), - author.to_string().color(self.text_colors.info), - author_nbr_commits.to_string().color(self.text_colors.info), - )); + author_field.push_str(&format!("{}\n", author)); } else { - author_field.push_str(&format!( - "{: = self.languages.iter().map(|(l, _)| format!("{}", l)).collect(); - let auths: Vec = self.authors.iter().map(|(l, _, _, _)| format!("{}", l)).collect(); state.serialize_field("repoName", &self.repo_name)?; state.serialize_field("numberOfTags", &self.number_of_tags)?; state.serialize_field("numberOfBranches", &self.number_of_branches)?; @@ -441,7 +434,7 @@ impl Serialize for Info { state.serialize_field("version", &self.version)?; state.serialize_field("creationDate", &self.creation_date)?; state.serialize_field("languages", &langs)?; - state.serialize_field("authors", &auths)?; + state.serialize_field("authors", &self.authors)?; state.serialize_field("lastChange", &self.last_change)?; state.serialize_field("repoUrl", &self.repo_url)?; state.serialize_field("numberOfCommits", &self.number_of_commits)?; diff --git a/src/info/repo.rs b/src/info/repo.rs index ae76b3d8c..7f659b43a 100644 --- a/src/info/repo.rs +++ b/src/info/repo.rs @@ -1,4 +1,5 @@ use crate::error::*; +use crate::info::author::Author; use crate::info::head_refs::HeadRefs; use byte_unit::Byte; use chrono::{FixedOffset, TimeZone}; @@ -87,7 +88,7 @@ impl<'a> Repo<'a> { &self, number_of_author: usize, show_email: bool, - ) -> Result, usize, usize)>> { + ) -> Result<(Vec, usize)> { let mut author_to_number_of_commits: HashMap = HashMap::new(); let mut total_nbr_of_commits = 0; let mailmap = self.repo.mailmap()?; @@ -102,29 +103,28 @@ impl<'a> Repo<'a> { total_nbr_of_commits += 1; } - let mut authors_sorted_by_number_of_commits: Vec<(Sig, usize)> = + let mut authors_by_number_of_commits: Vec<(Sig, usize)> = author_to_number_of_commits.into_iter().collect(); - authors_sorted_by_number_of_commits - .sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); - - authors_sorted_by_number_of_commits.truncate(number_of_author); - - let result: Vec<(String, Option, usize, usize)> = - authors_sorted_by_number_of_commits - .into_iter() - .map(|(author, author_nbr_of_commits)| { - ( - author.name.clone(), - show_email.then(|| author.email), - author_nbr_of_commits, - (author_nbr_of_commits as f32 * 100. / total_nbr_of_commits as f32).round() - as usize, - ) - }) - .collect(); - - Ok(result) + let number_of_authors = authors_by_number_of_commits.len(); + + authors_by_number_of_commits.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); + + authors_by_number_of_commits.truncate(number_of_author); + + let authors: Vec = authors_by_number_of_commits + .into_iter() + .map(|(author, author_nbr_of_commits)| { + Author::new( + author.name.clone(), + show_email.then(|| author.email), + author_nbr_of_commits, + total_nbr_of_commits, + ) + }) + .collect(); + + Ok((authors, number_of_authors)) } pub fn get_date_of_last_commit(&self, iso_time: bool) -> String { From 91bf11b9d480863f82e5b80835a99b075671bb2a Mon Sep 17 00:00:00 2001 From: o2sh Date: Sat, 3 Jul 2021 19:35:19 +0200 Subject: [PATCH 2/2] only truncate authors if necessary --- src/info/repo.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/info/repo.rs b/src/info/repo.rs index 7f659b43a..74d7502e8 100644 --- a/src/info/repo.rs +++ b/src/info/repo.rs @@ -86,7 +86,7 @@ impl<'a> Repo<'a> { pub fn get_authors( &self, - number_of_author: usize, + number_of_authors_to_display: usize, show_email: bool, ) -> Result<(Vec, usize)> { let mut author_to_number_of_commits: HashMap = HashMap::new(); @@ -110,7 +110,9 @@ impl<'a> Repo<'a> { authors_by_number_of_commits.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); - authors_by_number_of_commits.truncate(number_of_author); + if number_of_authors > number_of_authors_to_display { + authors_by_number_of_commits.truncate(number_of_authors_to_display); + } let authors: Vec = authors_by_number_of_commits .into_iter()