Skip to content

Commit d673169

Browse files
committed
Put all git call functions into a GitClient impl, minor refactoring
1 parent 929ea96 commit d673169

File tree

3 files changed

+127
-188
lines changed

3 files changed

+127
-188
lines changed

Diff for: src/onefetch/git_utils.rs

+103-166
Original file line numberDiff line numberDiff line change
@@ -1,195 +1,132 @@
1-
use crate::onefetch::{error::*, utils::bytes_to_human_readable};
2-
use chrono;
3-
use chrono::TimeZone;
4-
use chrono_humanize;
5-
use git2::{Commit, Repository, Time};
6-
use std::process::Command;
7-
use std::str;
8-
9-
pub fn get_git_history2(repo: &Repository, no_merges: bool) -> Result<Vec<Commit>> {
10-
let mut revwalk = repo.revwalk()?;
11-
revwalk.push_head()?;
12-
revwalk.set_sorting(git2::Sort::TIME)?;
13-
let commits: Vec<Commit> = revwalk
14-
.filter_map(|r| match r {
15-
Err(_) => return None,
16-
Ok(r) => {
17-
let commit = repo.find_commit(r).expect("Could not find commit");
18-
if no_merges {
19-
let parents = commit.parents().len();
20-
if parents > 1 {
21-
return None;
22-
}
23-
}
24-
Some(commit)
25-
}
26-
})
27-
.collect();
1+
use crate::onefetch::{error::*, utils};
2+
use git2::{Commit, Repository};
283

29-
Ok(commits)
4+
pub struct GitClient<'a> {
5+
repo: &'a Repository,
6+
commit_history: Vec<Commit<'a>>,
307
}
318

32-
pub fn get_git_history(dir: &str, no_merges: bool) -> Result<Vec<String>> {
33-
let mut args = vec!["-C", dir, "log"];
34-
if no_merges {
35-
args.push("--no-merges");
9+
impl<'a> GitClient<'a> {
10+
pub fn new(repo: &Repository, no_merges: bool) -> Result<GitClient> {
11+
let commit_history = GitClient::get_git_history(repo, no_merges)?;
12+
Ok(GitClient { repo: repo, commit_history: commit_history })
3613
}
3714

38-
args.push("--pretty=%cr\t%ae\t%an");
15+
pub fn get_creation_date(&self) -> Result<String> {
16+
let first_commit = self.commit_history.last();
3917

40-
let output = Command::new("git").args(args).output()?;
41-
42-
let output = String::from_utf8_lossy(&output.stdout);
43-
Ok(output.lines().map(|x| x.to_string()).collect::<Vec<_>>())
44-
}
18+
let output = match first_commit {
19+
Some(commit) => {
20+
let time = commit.time();
21+
utils::git_time_to_human_time(&time)
22+
}
23+
None => "".into(),
24+
};
4525

46-
pub fn get_authors2(git_history: &Vec<Commit>, n: usize) -> Vec<(String, usize, usize)> {
47-
let mut authors = std::collections::HashMap::new();
48-
let mut author_name_by_email = std::collections::HashMap::new();
49-
let mut total_commits = 0;
50-
for commit in git_history {
51-
let author = commit.author();
52-
let author_name = author.name().unwrap().to_string();
53-
let author_email = author.email().unwrap();
54-
55-
let commit_count = authors.entry(author_email.to_string()).or_insert(0);
56-
author_name_by_email.entry(author_email.to_string()).or_insert(author_name);
57-
*commit_count += 1;
58-
total_commits += 1;
26+
Ok(output)
5927
}
6028

61-
let mut authors: Vec<(String, usize)> = authors.into_iter().collect();
62-
authors.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count));
29+
pub fn get_number_of_commits(&self) -> String {
30+
let number_of_commits = self.commit_history.len();
31+
number_of_commits.to_string()
32+
}
6333

64-
authors.truncate(n);
34+
pub fn get_authors(&self, n: usize) -> Vec<(String, usize, usize)> {
35+
let mut authors = std::collections::HashMap::new();
36+
let mut author_name_by_email = std::collections::HashMap::new();
37+
let mut total_commits = 0;
38+
for commit in &self.commit_history {
39+
let author = commit.author();
40+
let author_name = author.name().unwrap().to_string();
41+
let author_email = author.email().unwrap();
42+
43+
let commit_count = authors.entry(author_email.to_string()).or_insert(0);
44+
author_name_by_email.entry(author_email.to_string()).or_insert(author_name);
45+
*commit_count += 1;
46+
total_commits += 1;
47+
}
6548

66-
let authors: Vec<(String, usize, usize)> = authors
67-
.into_iter()
68-
.map(|(author, count)| {
69-
(
70-
author_name_by_email.get(&author).unwrap().trim_matches('\'').to_string(),
71-
count,
72-
count * 100 / total_commits,
73-
)
74-
})
75-
.collect();
49+
let mut authors: Vec<(String, usize)> = authors.into_iter().collect();
50+
authors.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count));
7651

77-
authors
78-
}
52+
authors.truncate(n);
7953

80-
pub fn get_date_of_last_commit2(git_history: &Vec<Commit>) -> Result<String> {
81-
let last_commit = git_history.first();
54+
let authors: Vec<(String, usize, usize)> = authors
55+
.into_iter()
56+
.map(|(author, count)| {
57+
(
58+
author_name_by_email.get(&author).unwrap().trim_matches('\'').to_string(),
59+
count,
60+
count * 100 / total_commits,
61+
)
62+
})
63+
.collect();
8264

83-
let output = match last_commit {
84-
Some(commit) => git_time_to_human_time(&commit.time()),
85-
None => "".into(),
86-
};
87-
Ok(output)
88-
}
65+
authors
66+
}
8967

90-
fn git_time_to_human_time(time: &Time) -> String {
91-
let (offset, _) = match time.offset_minutes() {
92-
n if n < 0 => (-n, '-'),
93-
n => (n, '+'),
94-
};
68+
pub fn get_date_of_last_commit(&self) -> String {
69+
let last_commit = self.commit_history.first();
9570

96-
let offset = chrono::FixedOffset::west(offset);
97-
let dt_with_tz = offset.timestamp(time.seconds(), 0);
98-
let ht = chrono_humanize::HumanTime::from(dt_with_tz);
99-
format!("{}", ht.to_text_en(chrono_humanize::Accuracy::Rough, chrono_humanize::Tense::Past))
100-
}
71+
let output = match last_commit {
72+
Some(commit) => utils::git_time_to_human_time(&commit.time()),
73+
None => "".into(),
74+
};
75+
output
76+
}
10177

102-
pub fn get_creation_date2(git_history: &Vec<Commit>) -> Result<String> {
103-
let first_commit = git_history.last();
78+
// This collects the repo size excluding .git
79+
pub fn get_repo_size(&self) -> (String, u64) {
80+
let (repo_size, file_count) = match self.repo.index() {
81+
Ok(index) => {
82+
let mut repo_size: u128 = 0;
83+
let mut file_count: u64 = 0;
84+
for index_entry in index.iter() {
85+
file_count += 1;
86+
repo_size += index_entry.file_size as u128;
87+
}
88+
(repo_size, file_count)
89+
}
90+
Err(_) => (0, 0),
91+
};
10492

105-
let output = match first_commit {
106-
Some(commit) => {
107-
let time = commit.time();
108-
git_time_to_human_time(&time)
109-
}
110-
None => "".into(),
111-
};
93+
(utils::bytes_to_human_readable(repo_size), file_count)
94+
}
11295

113-
Ok(output)
114-
}
96+
fn get_git_history(repo: &'a Repository, no_merges: bool) -> Result<Vec<Commit<'a>>> {
97+
let mut revwalk = repo.revwalk()?;
98+
revwalk.push_head()?;
99+
revwalk.set_sorting(git2::Sort::TIME)?;
100+
let commits: Vec<Commit<'a>> = revwalk
101+
.filter_map(|r| match r {
102+
Err(_) => return None,
103+
Ok(r) => {
104+
let commit = repo.find_commit(r).expect("Could not find commit");
105+
if no_merges {
106+
let parents = commit.parents().len();
107+
if parents > 1 {
108+
return None;
109+
}
110+
}
111+
Some(commit.to_owned())
112+
}
113+
})
114+
.collect();
115115

116-
pub fn get_number_of_commits2(git_history: &Vec<Commit>) -> String {
117-
let number_of_commits = git_history.len();
118-
number_of_commits.to_string()
116+
Ok(commits)
117+
}
119118
}
120119

121-
pub fn get_packed_size(repo_size: String, files_count: Option<u64>) -> Result<String> {
120+
// Should be moved to fmt::Display of Info
121+
pub fn get_packed_size(repo_size: String, files_count: u64) -> Result<String> {
122122
match files_count {
123-
Some(files_count) => {
124-
let res = format!("{} ({} files)", repo_size, files_count.to_string());
125-
Ok(res)
126-
}
127-
None => {
123+
0 => {
128124
let res = repo_size;
129125
Ok(res.into())
130126
}
131-
}
132-
}
133-
134-
// This collects the repo size excluding .git
135-
pub fn get_repo_size2(repo: &Repository) -> String {
136-
let repo_size = match repo.index() {
137-
Ok(index) => {
138-
let mut repo_size: u128 = 0;
139-
for index_entry in index.iter() {
140-
println!("{}", str::from_utf8(&index_entry.path).unwrap());
141-
repo_size += index_entry.file_size as u128;
142-
}
143-
repo_size
144-
}
145-
Err(_) => 0,
146-
};
147-
148-
bytes_to_human_readable(repo_size)
149-
}
150-
151-
pub fn get_repo_size(dir: &str) -> String {
152-
let output = Command::new("git")
153-
.arg("-C")
154-
.arg(dir)
155-
.arg("count-objects")
156-
.arg("-vH")
157-
.output()
158-
.expect("Failed to execute git.");
159-
160-
let output = String::from_utf8_lossy(&output.stdout);
161-
let lines = output.to_string();
162-
let size_line = lines.split('\n').find(|line| line.starts_with("size-pack:"));
163-
164-
let repo_size = match size_line {
165-
None => String::new(),
166-
Some(size_str) => String::from(&(size_str[11..])),
167-
};
168-
repo_size
169-
}
170-
171-
pub fn get_files_count(dir: &str) -> Option<u64> {
172-
let output = Command::new("git")
173-
.arg("-C")
174-
.arg(dir)
175-
.arg("ls-files")
176-
.output()
177-
.expect("Failed to execute git.");
178-
// To check if command executed successfully or not
179-
let error = &output.stderr;
180-
181-
if error.is_empty() {
182-
let output = String::from_utf8_lossy(&output.stdout);
183-
184-
let lines = output.to_string();
185-
let files_list = lines.split('\n');
186-
let mut files_count: u64 = 0;
187-
for _file in files_list {
188-
files_count += 1;
127+
_ => {
128+
let res = format!("{} ({} files)", repo_size, files_count.to_string());
129+
Ok(res)
189130
}
190-
files_count -= 1; // As splitting giving one line extra(blank).
191-
Some(files_count)
192-
} else {
193-
None
194131
}
195132
}

Diff for: src/onefetch/info.rs

+9-22
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub struct Info {
2727
number_of_commits: String,
2828
lines_of_code: usize,
2929
packed_repo_size: String,
30-
files_count: Option<u64>,
30+
files_count: u64,
3131
repo_size: String,
3232
license: String,
3333
pub dominant_language: Language,
@@ -223,21 +223,13 @@ impl Info {
223223
let number_of_branches = repo.get_number_of_branches()?;
224224

225225
let repo = Repository::open(&workdir)?;
226-
let git_history2 = git_utils::get_git_history2(&repo, config.no_merges)?;
227-
let creation_date = git_utils::get_creation_date2(&git_history2)?;
228-
let number_of_commits = git_utils::get_number_of_commits2(&git_history2);
229-
let authors = git_utils::get_authors2(&git_history2, config.number_of_authors);
230-
let last_change = git_utils::get_date_of_last_commit2(&git_history2)?;
231-
// let repo_size = git_utils::get_repo_size2(&repo);
232-
233-
// Git Calls
234-
// let git_history = git_utils::get_git_history(&workdir, config.no_merges)?;
235-
// let creation_date = git_utils::get_creation_date(&git_history)?;
236-
// let number_of_commits = git_utils::get_number_of_commits(&git_history);
237-
// let authors = git_utils::get_authors(&git_history, config.number_of_authors);
238-
// let last_change = git_utils::get_date_of_last_commit(&git_history)?;
239-
let repo_size = git_utils::get_repo_size(&workdir);
240-
let files_count = git_utils::get_files_count(&workdir);
226+
227+
let git_client = git_utils::GitClient::new(&repo, config.no_merges)?;
228+
let creation_date = git_client.get_creation_date()?;
229+
let number_of_commits = git_client.get_number_of_commits();
230+
let authors = git_client.get_authors(config.number_of_authors);
231+
let last_change = git_client.get_date_of_last_commit();
232+
let (repo_size, files_count) = git_client.get_repo_size();
241233
let packed_repo_size = git_utils::get_packed_size(repo_size.clone(), files_count)?;
242234

243235
let git_version = cli_utils::get_git_version()?;
@@ -425,12 +417,7 @@ impl Serialize for Info {
425417
state.serialize_field("linesOfCode", &self.lines_of_code)?;
426418
state.serialize_field("packedRepoSize", &self.packed_repo_size)?;
427419
state.serialize_field("repoSize", &self.repo_size)?;
428-
429-
match &self.files_count {
430-
Some(files_count) => state.serialize_field("filesCount", files_count)?,
431-
None => {}
432-
}
433-
420+
state.serialize_field("filesCount", &self.files_count)?;
434421
state.serialize_field("license", &self.license)?;
435422
state.serialize_field("dominantLanguage", &self.dominant_language)?;
436423
state.serialize_field("textColors", &self.text_colors)?;

Diff for: src/onefetch/utils.rs

+15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use byte_unit::Byte;
2+
use chrono::{FixedOffset, TimeZone};
3+
use chrono_humanize::{Accuracy, HumanTime, Tense};
24
use colored::Color;
5+
use git2::Time;
36

47
pub fn num_to_color(num: &str) -> Option<Color> {
58
let color = match num {
@@ -28,3 +31,15 @@ pub fn bytes_to_human_readable(bytes: u128) -> String {
2831
let byte = Byte::from_bytes(bytes);
2932
byte.get_appropriate_unit(true).to_string()
3033
}
34+
35+
pub fn git_time_to_human_time(time: &Time) -> String {
36+
let (offset, _) = match time.offset_minutes() {
37+
n if n < 0 => (-n, '-'),
38+
n => (n, '+'),
39+
};
40+
41+
let offset = FixedOffset::west(offset);
42+
let dt_with_tz = offset.timestamp(time.seconds(), 0);
43+
let ht = HumanTime::from(dt_with_tz);
44+
format!("{}", ht.to_text_en(Accuracy::Rough, Tense::Past))
45+
}

0 commit comments

Comments
 (0)