Skip to content

Commit 4028f8c

Browse files
authored
display the number of contributors (#456)
* display number of contributors * only truncate authors if necessary
1 parent 9739ddc commit 4028f8c

File tree

4 files changed

+99
-58
lines changed

4 files changed

+99
-58
lines changed

src/info/author.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use serde::ser::SerializeStruct;
2+
use serde::Serialize;
3+
4+
pub struct Author {
5+
name: String,
6+
email: Option<String>,
7+
nbr_of_commits: usize,
8+
contribution: usize,
9+
}
10+
11+
impl Author {
12+
pub fn new(
13+
name: String,
14+
email: Option<String>,
15+
nbr_of_commits: usize,
16+
total_nbr_of_commits: usize,
17+
) -> Self {
18+
let contribution =
19+
(nbr_of_commits as f32 * 100. / total_nbr_of_commits as f32).round() as usize;
20+
Self { name, email, nbr_of_commits, contribution }
21+
}
22+
}
23+
24+
impl std::fmt::Display for Author {
25+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
26+
if let Some(email) = &self.email {
27+
write!(f, "{}% {} <{}> {}", self.contribution, self.name, email, self.nbr_of_commits)
28+
} else {
29+
write!(f, "{}% {} {}", self.contribution, self.name, self.nbr_of_commits)
30+
}
31+
}
32+
}
33+
34+
impl Serialize for Author {
35+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
36+
where
37+
S: serde::Serializer,
38+
{
39+
let mut state = serializer.serialize_struct("Author", 1)?;
40+
state.serialize_field("name", &self.name)?;
41+
state.end()
42+
}
43+
}

src/info/info_field.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,16 @@ pub enum InfoField {
88
GitInfo,
99
Project,
1010
HEAD,
11+
Pending,
1112
Version,
1213
Created,
13-
Dependencies,
1414
Languages,
15+
Dependencies,
1516
Authors,
1617
LastChange,
18+
Contributors,
1719
Repo,
1820
Commits,
19-
Pending,
2021
LinesOfCode,
2122
Size,
2223
License,
@@ -27,15 +28,16 @@ pub struct InfoFieldOff {
2728
pub git_info: bool,
2829
pub project: bool,
2930
pub head: bool,
31+
pub pending: bool,
3032
pub version: bool,
3133
pub created: bool,
32-
pub dependencies: bool,
3334
pub languages: bool,
35+
pub dependencies: bool,
3436
pub authors: bool,
3537
pub last_change: bool,
38+
pub contributors: bool,
3639
pub repo: bool,
3740
pub commits: bool,
38-
pub pending: bool,
3941
pub lines_of_code: bool,
4042
pub size: bool,
4143
pub license: bool,
@@ -52,14 +54,15 @@ impl InfoFieldOff {
5254
InfoField::GitInfo => info_field_off.git_info = true,
5355
InfoField::Project => info_field_off.project = true,
5456
InfoField::HEAD => info_field_off.head = true,
57+
InfoField::Pending => info_field_off.pending = true,
5558
InfoField::Version => info_field_off.version = true,
5659
InfoField::Created => info_field_off.created = true,
57-
InfoField::Dependencies => info_field_off.dependencies = true,
5860
InfoField::Languages => info_field_off.languages = true,
61+
InfoField::Dependencies => info_field_off.dependencies = true,
5962
InfoField::Authors => info_field_off.authors = true,
6063
InfoField::LastChange => info_field_off.last_change = true,
64+
InfoField::Contributors => info_field_off.contributors = true,
6165
InfoField::Repo => info_field_off.repo = true,
62-
InfoField::Pending => info_field_off.pending = true,
6366
InfoField::Commits => info_field_off.commits = true,
6467
InfoField::LinesOfCode => info_field_off.lines_of_code = true,
6568
InfoField::Size => info_field_off.size = true,

src/info/mod.rs

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::cli::{self, Config};
22
use crate::error::*;
33
use crate::ui::get_ascii_colors;
44
use crate::ui::text_color::TextColor;
5+
use author::Author;
56
use colored::{Color, ColoredString, Colorize};
67
use deps::DependencyDetector;
78
use git2::Repository;
@@ -12,6 +13,7 @@ use repo::Repo;
1213
use serde::ser::SerializeStruct;
1314
use serde::Serialize;
1415

16+
mod author;
1517
pub mod deps;
1618
mod head_refs;
1719
pub mod info_field;
@@ -31,8 +33,9 @@ pub struct Info {
3133
creation_date: String,
3234
languages: Vec<(Language, f64)>,
3335
dependencies: String,
34-
authors: Vec<(String, Option<String>, usize, usize)>,
36+
authors: Vec<Author>,
3537
last_change: String,
38+
contributors: usize,
3639
repo_url: String,
3740
number_of_commits: String,
3841
lines_of_code: usize,
@@ -151,6 +154,17 @@ impl std::fmt::Display for Info {
151154
)?;
152155
}
153156

157+
if !self.config.disabled_fields.contributors
158+
&& self.contributors > self.config.number_of_authors
159+
{
160+
writeln!(
161+
f,
162+
"{}{}",
163+
&self.get_formatted_subtitle_label("Contributors"),
164+
&self.contributors.to_string().color(self.text_colors.info),
165+
)?;
166+
}
167+
154168
if !self.config.disabled_fields.repo && !self.repo_url.is_empty() {
155169
writeln!(
156170
f,
@@ -231,7 +245,8 @@ impl Info {
231245
let number_of_branches = internal_repo.get_number_of_branches()?;
232246
let creation_date = internal_repo.get_creation_date(config.iso_time)?;
233247
let number_of_commits = internal_repo.get_number_of_commits();
234-
let authors = internal_repo.get_authors(config.number_of_authors, config.show_email)?;
248+
let (authors, contributors) =
249+
internal_repo.get_authors(config.number_of_authors, config.show_email)?;
235250
let last_change = internal_repo.get_date_of_last_commit(config.iso_time);
236251
let (repo_size, file_count) = internal_repo.get_repo_size();
237252
let workdir = internal_repo.get_work_dir()?;
@@ -262,6 +277,7 @@ impl Info {
262277
dependencies,
263278
authors,
264279
last_change,
280+
contributors,
265281
repo_url,
266282
number_of_commits,
267283
lines_of_code,
@@ -322,33 +338,11 @@ impl Info {
322338

323339
let pad = title.len() + 2;
324340

325-
for (i, (author_name, author_email_opt, author_nbr_commits, autor_contribution)) in
326-
self.authors.iter().enumerate()
327-
{
328-
let author = if let Some(author_email) = author_email_opt {
329-
format!("{} <{}>", author_name, author_email)
330-
} else {
331-
author_name.to_owned()
332-
};
333-
341+
for (i, author) in self.authors.iter().enumerate() {
334342
if i == 0 {
335-
author_field.push_str(&format!(
336-
"{}{} {} {}\n",
337-
autor_contribution.to_string().color(self.text_colors.info),
338-
"%".color(self.text_colors.info),
339-
author.to_string().color(self.text_colors.info),
340-
author_nbr_commits.to_string().color(self.text_colors.info),
341-
));
343+
author_field.push_str(&format!("{}\n", author));
342344
} else {
343-
author_field.push_str(&format!(
344-
"{:<width$}{}{} {} {}\n",
345-
"",
346-
autor_contribution.to_string().color(self.text_colors.info),
347-
"%".color(self.text_colors.info),
348-
author.to_string().color(self.text_colors.info),
349-
author_nbr_commits.to_string().color(self.text_colors.info),
350-
width = pad
351-
));
345+
author_field.push_str(&format!("{:<width$}{}\n", "", author, width = pad));
352346
}
353347
}
354348

@@ -433,15 +427,14 @@ impl Serialize for Info {
433427
{
434428
let mut state = serializer.serialize_struct("Info", 15)?;
435429
let langs: Vec<String> = self.languages.iter().map(|(l, _)| format!("{}", l)).collect();
436-
let auths: Vec<String> = self.authors.iter().map(|(l, _, _, _)| format!("{}", l)).collect();
437430
state.serialize_field("repoName", &self.repo_name)?;
438431
state.serialize_field("numberOfTags", &self.number_of_tags)?;
439432
state.serialize_field("numberOfBranches", &self.number_of_branches)?;
440433
state.serialize_field("headRefs", &self.head_refs)?;
441434
state.serialize_field("version", &self.version)?;
442435
state.serialize_field("creationDate", &self.creation_date)?;
443436
state.serialize_field("languages", &langs)?;
444-
state.serialize_field("authors", &auths)?;
437+
state.serialize_field("authors", &self.authors)?;
445438
state.serialize_field("lastChange", &self.last_change)?;
446439
state.serialize_field("repoUrl", &self.repo_url)?;
447440
state.serialize_field("numberOfCommits", &self.number_of_commits)?;

src/info/repo.rs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::error::*;
2+
use crate::info::author::Author;
23
use crate::info::head_refs::HeadRefs;
34
use byte_unit::Byte;
45
use chrono::{FixedOffset, TimeZone};
@@ -85,9 +86,9 @@ impl<'a> Repo<'a> {
8586

8687
pub fn get_authors(
8788
&self,
88-
number_of_author: usize,
89+
number_of_authors_to_display: usize,
8990
show_email: bool,
90-
) -> Result<Vec<(String, Option<String>, usize, usize)>> {
91+
) -> Result<(Vec<Author>, usize)> {
9192
let mut author_to_number_of_commits: HashMap<Sig, usize> = HashMap::new();
9293
let mut total_nbr_of_commits = 0;
9394
let mailmap = self.repo.mailmap()?;
@@ -102,29 +103,30 @@ impl<'a> Repo<'a> {
102103
total_nbr_of_commits += 1;
103104
}
104105

105-
let mut authors_sorted_by_number_of_commits: Vec<(Sig, usize)> =
106+
let mut authors_by_number_of_commits: Vec<(Sig, usize)> =
106107
author_to_number_of_commits.into_iter().collect();
107108

108-
authors_sorted_by_number_of_commits
109-
.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count));
110-
111-
authors_sorted_by_number_of_commits.truncate(number_of_author);
112-
113-
let result: Vec<(String, Option<String>, usize, usize)> =
114-
authors_sorted_by_number_of_commits
115-
.into_iter()
116-
.map(|(author, author_nbr_of_commits)| {
117-
(
118-
author.name.clone(),
119-
show_email.then(|| author.email),
120-
author_nbr_of_commits,
121-
(author_nbr_of_commits as f32 * 100. / total_nbr_of_commits as f32).round()
122-
as usize,
123-
)
124-
})
125-
.collect();
126-
127-
Ok(result)
109+
let number_of_authors = authors_by_number_of_commits.len();
110+
111+
authors_by_number_of_commits.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count));
112+
113+
if number_of_authors > number_of_authors_to_display {
114+
authors_by_number_of_commits.truncate(number_of_authors_to_display);
115+
}
116+
117+
let authors: Vec<Author> = authors_by_number_of_commits
118+
.into_iter()
119+
.map(|(author, author_nbr_of_commits)| {
120+
Author::new(
121+
author.name.clone(),
122+
show_email.then(|| author.email),
123+
author_nbr_of_commits,
124+
total_nbr_of_commits,
125+
)
126+
})
127+
.collect();
128+
129+
Ok((authors, number_of_authors))
128130
}
129131

130132
pub fn get_date_of_last_commit(&self, iso_time: bool) -> String {

0 commit comments

Comments
 (0)