diff --git a/src/info/mod.rs b/src/info/mod.rs index f5664a844..481b50286 100644 --- a/src/info/mod.rs +++ b/src/info/mod.rs @@ -231,7 +231,7 @@ 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 = 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()?; @@ -431,31 +431,17 @@ impl Serialize for Info { where S: serde::Serializer, { - let mut state = serializer.serialize_struct("Info", 20)?; - - // Only collect the version number - let git_version = if !self.git_version.is_empty() { - self.git_version.split(' ').collect::>()[2] - } else { - "" - }; - - state.serialize_field("gitVersion", &git_version)?; - state.serialize_field("gitUsername", &self.git_username)?; + let mut state = serializer.serialize_struct("Info", 15)?; + let langs: Vec = 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)?; state.serialize_field("headRefs", &self.head_refs)?; - state.serialize_field("pendingChanges", &self.pending_changes)?; state.serialize_field("version", &self.version)?; state.serialize_field("creationDate", &self.creation_date)?; - state.serialize_field("languages", &self.languages)?; - - let dependencies_split: Vec = - self.dependencies.split(' ').map(|s| s.to_string()).collect(); - - state.serialize_field("dependencies", &dependencies_split[0])?; - state.serialize_field("authors", &self.authors)?; + state.serialize_field("languages", &langs)?; + state.serialize_field("authors", &auths)?; state.serialize_field("lastChange", &self.last_change)?; state.serialize_field("repoUrl", &self.repo_url)?; state.serialize_field("numberOfCommits", &self.number_of_commits)?; @@ -463,7 +449,7 @@ impl Serialize for Info { state.serialize_field("repoSize", &self.repo_size)?; state.serialize_field("filesCount", &self.file_count)?; state.serialize_field("license", &self.license)?; - state.serialize_field("dominantLanguage", &self.dominant_language)?; + state.end() } } diff --git a/src/info/repo.rs b/src/info/repo.rs index 06eed2aa3..ae76b3d8c 100644 --- a/src/info/repo.rs +++ b/src/info/repo.rs @@ -9,6 +9,7 @@ use git2::{ StatusShow, }; use regex::Regex; +use std::collections::HashMap; use std::path::Path; pub struct Repo<'a> { @@ -16,6 +17,20 @@ pub struct Repo<'a> { logs: Vec>, } +#[derive(Hash, PartialEq, Eq)] +pub struct Sig { + name: String, + email: String, +} + +impl From> for Sig { + fn from(sig: Signature) -> Self { + let name = String::from_utf8_lossy(sig.name_bytes()).into_owned(); + let email = String::from_utf8_lossy(sig.email_bytes()).into_owned(); + Self { name, email } + } +} + impl<'a> Repo<'a> { pub fn new( repo: &'a Repository, @@ -70,42 +85,46 @@ impl<'a> Repo<'a> { pub fn get_authors( &self, - n: usize, + number_of_author: usize, show_email: bool, - ) -> Vec<(String, Option, usize, usize)> { - let mut authors = std::collections::HashMap::new(); - let mut author_name_by_email = std::collections::HashMap::new(); + ) -> Result, usize, usize)>> { + let mut author_to_number_of_commits: HashMap = HashMap::new(); let mut total_nbr_of_commits = 0; + let mailmap = self.repo.mailmap()?; for commit in &self.logs { - let author = commit.author(); - let author_name = String::from_utf8_lossy(author.name_bytes()).into_owned(); - let author_email = String::from_utf8_lossy(author.email_bytes()).into_owned(); - - let author_nbr_of_commits = authors.entry(author_email.to_string()).or_insert(0); - author_name_by_email.entry(author_email.to_string()).or_insert(author_name); + let author = match commit.author_with_mailmap(&mailmap) { + Ok(val) => val, + Err(_) => commit.author(), + }; + let author_nbr_of_commits = + author_to_number_of_commits.entry(Sig::from(author)).or_insert(0); *author_nbr_of_commits += 1; total_nbr_of_commits += 1; } - let mut authors: Vec<(String, usize)> = authors.into_iter().collect(); - authors.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count)); - - authors.truncate(n); - - let authors: Vec<(String, Option, usize, usize)> = authors - .into_iter() - .map(|(author_email, author_nbr_of_commits)| { - ( - author_name_by_email.get(&author_email).unwrap().trim_matches('\'').to_string(), - 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(); - - authors + let mut authors_sorted_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) } pub fn get_date_of_last_commit(&self, iso_time: bool) -> String {