From e24b80b451aaba3da388cb74d35a675b08ecee2f Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Thu, 29 Apr 2021 22:30:53 +0800 Subject: [PATCH 01/12] calculating daily work time --- src/main.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main.rs b/src/main.rs index 2687dc2..ac5bd2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -371,6 +371,7 @@ fn filter_commits<'repo>(config: &Config, commits: Vec>) -> Vec, author_name: Option, + breakdown: HashMap, duration: Duration, commit_count: usize } @@ -380,16 +381,25 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com commits.sort_by(|a, b| a.time().cmp(&b.time())); + let mut coding_session_start = Utc.timestamp(commits[0].time().seconds(), 0).format("%Y-%m-%d"); let len = commits.len() - 1; let all_but_last = commits.iter().enumerate().take(len); + let mut breakdown = HashMap::new(); let duration = all_but_last.fold(Duration::minutes(0), |acc, (i, commit)| { let next_commit = commits.get(i+1).unwrap(); let diff_seconds = next_commit.time().seconds() - commit.time().seconds(); let dur = Duration::seconds(diff_seconds); if dur < *max_commit_diff { + breakdown.entry(coding_session_start.to_string()) + .and_modify(|e| { *e = *e + dur }) + .or_insert_with(|| dur); acc + dur } else { + coding_session_start = Utc.timestamp(commit.time().seconds(), 0).format("%Y-%m-%d"); + breakdown.entry(coding_session_start.to_string()) + .and_modify(|e| { *e = *e + *first_commit_addition }) + .or_insert_with(|| dur); acc + *first_commit_addition } }); @@ -397,6 +407,7 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com CommitHours { email: email, author_name: author_name, + breakdown: breakdown, duration: duration, commit_count: commits.len() } From d835663412946067090d4379d08532134584f0a1 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Fri, 30 Apr 2021 19:29:58 +0800 Subject: [PATCH 02/12] including breakdown in the json output --- src/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main.rs b/src/main.rs index ac5bd2e..0cba292 100644 --- a/src/main.rs +++ b/src/main.rs @@ -519,15 +519,24 @@ fn print_results_stdout(times: &Vec) -> Result<()> { struct CommitHoursJson { email: Option, author_name: Option, + breakdown: Option>, hours: f32, commit_count: usize } impl From<&CommitHours> for CommitHoursJson { fn from(time: &CommitHours) -> Self { + let mut breakdown = HashMap::new(); + //*(breakdown) = &(time.breakdown).into_iter().filter_map(|(key, value)| { + // Some((key, value.num_minutes() as f32 / 60.0)) + //}).collect(); + for (key, value) in &(time.breakdown) { + breakdown.insert(key.to_owned(), value.num_minutes() as f32 / 60.0); + } CommitHoursJson { email: time.email.clone(), author_name: time.author_name.clone(), + breakdown: Some(breakdown), hours: time.duration.num_minutes() as f32 / 60.0, commit_count: time.commit_count, } @@ -541,6 +550,7 @@ fn print_results_json(times: &Vec) -> Result<()> { times_json.push(CommitHoursJson { email: None, author_name: Some(String::from("Total")), + breakdown: None, hours: total_estimated_hours, commit_count: total_commits }); From 3754280ebf8df51429762a6404a6d599a67db366 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Sat, 1 May 2021 09:36:05 +0800 Subject: [PATCH 03/12] resolving a bug causing breakdown not summing up to the total --- src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0cba292..52fcb64 100644 --- a/src/main.rs +++ b/src/main.rs @@ -399,7 +399,7 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com coding_session_start = Utc.timestamp(commit.time().seconds(), 0).format("%Y-%m-%d"); breakdown.entry(coding_session_start.to_string()) .and_modify(|e| { *e = *e + *first_commit_addition }) - .or_insert_with(|| dur); + .or_insert_with(|| *first_commit_addition); acc + *first_commit_addition } }); @@ -527,9 +527,6 @@ struct CommitHoursJson { impl From<&CommitHours> for CommitHoursJson { fn from(time: &CommitHours) -> Self { let mut breakdown = HashMap::new(); - //*(breakdown) = &(time.breakdown).into_iter().filter_map(|(key, value)| { - // Some((key, value.num_minutes() as f32 / 60.0)) - //}).collect(); for (key, value) in &(time.breakdown) { breakdown.insert(key.to_owned(), value.num_minutes() as f32 / 60.0); } From 5a6df2cf8b5fcd75846a245918ca43ecdcaac7a6 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Sat, 1 May 2021 09:56:13 +0800 Subject: [PATCH 04/12] make breakdown optional depending on a presence of a command line flag --- src/main.rs | 36 +++++++++++++++++++++++++----------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/src/main.rs b/src/main.rs index 52fcb64..0b7598a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -133,7 +133,10 @@ struct Config { branch_type: BranchType, /// Output format. - output_format: OutputFormat + output_format: OutputFormat, + + // Display breakdown + display_breakdown: bool } fn get_app<'a, 'b>() -> App<'a, 'b> { @@ -205,6 +208,11 @@ fn get_app<'a, 'b>() -> App<'a, 'b> { .required(true) .default_value(".") .index(1)) + .arg(Arg::with_name("breakdown") + .long("breakdown") + .short("w") + .help("Display number of work hours per day.") + .takes_value(false)) } fn parse_email_alias(s: &str) -> Result<(String, String)> { @@ -292,6 +300,7 @@ fn to_config(matches: ArgMatches) -> Result { }; let merge_requests = matches.is_present("merge-requests"); + let display_breakdown = matches.is_present("breakdown"); let git_repo_path = matches.value_of("REPO_PATH").unwrap(); @@ -331,7 +340,8 @@ fn to_config(matches: ArgMatches) -> Result { email_aliases: aliases, branch: branch, branch_type: branch_type, - output_format: output_format + output_format: output_format, + display_breakdown: display_breakdown }) } @@ -376,7 +386,7 @@ struct CommitHours { commit_count: usize } -fn estimate_author_time(mut commits: Vec, email: Option, max_commit_diff: &Duration, first_commit_addition: &Duration) -> CommitHours { +fn estimate_author_time(mut commits: Vec, email: Option, max_commit_diff: &Duration, first_commit_addition: &Duration, display_breakdown: &bool) -> CommitHours { let author_name = commits[0].author().name().map(|n| n.to_string()); commits.sort_by(|a, b| a.time().cmp(&b.time())); @@ -391,15 +401,19 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com let dur = Duration::seconds(diff_seconds); if dur < *max_commit_diff { - breakdown.entry(coding_session_start.to_string()) - .and_modify(|e| { *e = *e + dur }) - .or_insert_with(|| dur); + if *display_breakdown { + breakdown.entry(coding_session_start.to_string()) + .and_modify(|e| { *e = *e + dur }) + .or_insert_with(|| dur); + } acc + dur } else { coding_session_start = Utc.timestamp(commit.time().seconds(), 0).format("%Y-%m-%d"); - breakdown.entry(coding_session_start.to_string()) - .and_modify(|e| { *e = *e + *first_commit_addition }) - .or_insert_with(|| *first_commit_addition); + if *display_breakdown { + breakdown.entry(coding_session_start.to_string()) + .and_modify(|e| { *e = *e + *first_commit_addition }) + .or_insert_with(|| *first_commit_addition); + } acc + *first_commit_addition } }); @@ -443,11 +457,11 @@ fn estimate_author_times(config: &Config, commits: Vec) -> Vec 0 { - result.push(estimate_author_time(no_email, None, &config.max_commit_diff, &config.first_commit_addition)); + result.push(estimate_author_time(no_email, None, &config.max_commit_diff, &config.first_commit_addition, &config.display_breakdown)); } for (email, author_commits) in by_email { - result.push(estimate_author_time(author_commits, Some(email), &config.max_commit_diff, &config.first_commit_addition)); + result.push(estimate_author_time(author_commits, Some(email), &config.max_commit_diff, &config.first_commit_addition, &config.display_breakdown)); } result.sort_by(|a, b| { From 979d28c58dce6933b8ebdf7981b793ff02650466 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Sat, 1 May 2021 10:26:12 +0800 Subject: [PATCH 05/12] standard out table with daily work breakdown --- src/main.rs | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 0b7598a..142a71b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -529,6 +529,59 @@ fn print_results_stdout(times: &Vec) -> Result<()> { Ok(()) } +fn print_breakdown_results_stdout(times: &Vec) -> Result<()> { + let mut table = Table::new(); + + let format = format::FormatBuilder::new() + .column_separator('|') + .borders('|') + .separators(&[format::LinePosition::Top, + format::LinePosition::Bottom], + format::LineSeparator::new('-', '+', '+', '+')) + .padding(1, 1) + .build(); + table.set_format(format); + + table.set_titles(row!["Author", "Email", "Commits", "Date", "Estimated Hours"]); + table.add_empty_row(); + + for time in times.iter() { + let author = match &time.author_name { + Some(n) => n, + None => "" + }; + let email = match &time.email { + Some(email) => email, + None => "(none)" + }; + let commits = time.commit_count; + let estimated_total_hours = (time.duration.num_minutes() as f32) / 60.0; + + let mut first_row = true; + for (date, duration) in &(time.breakdown) { + let date = date.to_owned(); + let work_time = duration.num_minutes() as f32 / 60.0; + if first_row { + table.add_row(row!["", "", "", date, work_time]); + first_row = false; + } else { + table.add_row(row!["", "", "", date, work_time]); + } + } + table.add_row(row![author, email, commits, "Total", estimated_total_hours]); + table.add_empty_row(); + } + + table.add_empty_row(); + + let (total_estimated_hours, total_commits) = get_totals(times); + table.add_row(row!["Total", "", total_commits, "", total_estimated_hours]); + + table.printstd(); + + Ok(()) +} + #[derive(Clone, Serialize, Deserialize)] struct CommitHoursJson { email: Option, @@ -573,9 +626,12 @@ fn print_results_json(times: &Vec) -> Result<()> { Ok(()) } -fn print_results(times: &Vec, output_format: &OutputFormat) -> Result<()> { +fn print_results(times: &Vec, output_format: &OutputFormat, display_breakdown: &bool) -> Result<()> { match output_format { - OutputFormat::Stdout => print_results_stdout(times), + OutputFormat::Stdout => match *display_breakdown { + true => print_breakdown_results_stdout(times), + false => print_results_stdout(times) + }, OutputFormat::Json => print_results_json(times) } } @@ -604,7 +660,7 @@ fn jikyuu(config: &Config) -> Result { } Ok(1) } else { - print_results(&by_author, &config.output_format)?; + print_results(&by_author, &config.output_format, &config.display_breakdown)?; Ok(0) } } From 0b4d1200eaeb2b6c8fca573ee82c126621c43d2c Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Sat, 1 May 2021 10:27:34 +0800 Subject: [PATCH 06/12] no longer any need for distinguishing the first row --- src/main.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 142a71b..85732a8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -557,16 +557,10 @@ fn print_breakdown_results_stdout(times: &Vec) -> Result<()> { let commits = time.commit_count; let estimated_total_hours = (time.duration.num_minutes() as f32) / 60.0; - let mut first_row = true; for (date, duration) in &(time.breakdown) { let date = date.to_owned(); let work_time = duration.num_minutes() as f32 / 60.0; - if first_row { - table.add_row(row!["", "", "", date, work_time]); - first_row = false; - } else { - table.add_row(row!["", "", "", date, work_time]); - } + table.add_row(row!["", "", "", date, work_time]); } table.add_row(row![author, email, commits, "Total", estimated_total_hours]); table.add_empty_row(); From 480fc5f9ef2ac4c96dee938ab1dd5b103303873d Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Mon, 17 May 2021 11:14:02 +0800 Subject: [PATCH 07/12] accumulating values of breakdown hashmap inside of the fold and summing separately --- src/main.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/main.rs b/src/main.rs index 85732a8..da17396 100644 --- a/src/main.rs +++ b/src/main.rs @@ -394,28 +394,25 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com let mut coding_session_start = Utc.timestamp(commits[0].time().seconds(), 0).format("%Y-%m-%d"); let len = commits.len() - 1; let all_but_last = commits.iter().enumerate().take(len); - let mut breakdown = HashMap::new(); - let duration = all_but_last.fold(Duration::minutes(0), |acc, (i, commit)| { + let breakdown = all_but_last.fold(HashMap::new(), |mut breakdown, (i, commit)| { let next_commit = commits.get(i+1).unwrap(); let diff_seconds = next_commit.time().seconds() - commit.time().seconds(); let dur = Duration::seconds(diff_seconds); - if dur < *max_commit_diff { - if *display_breakdown { - breakdown.entry(coding_session_start.to_string()) - .and_modify(|e| { *e = *e + dur }) - .or_insert_with(|| dur); - } - acc + dur + breakdown.entry(coding_session_start.to_string()) + .and_modify(|e| { *e = *e + dur }) + .or_insert_with(|| dur); } else { coding_session_start = Utc.timestamp(commit.time().seconds(), 0).format("%Y-%m-%d"); - if *display_breakdown { - breakdown.entry(coding_session_start.to_string()) - .and_modify(|e| { *e = *e + *first_commit_addition }) - .or_insert_with(|| *first_commit_addition); - } - acc + *first_commit_addition + breakdown.entry(coding_session_start.to_string()) + .and_modify(|e| { *e = *e + *first_commit_addition }) + .or_insert_with(|| *first_commit_addition); } + breakdown + }); + + let duration = breakdown.values().fold(Duration::minutes(0), |acc, dur| { + acc + *dur }); CommitHours { From 978000e77df52d39e04cae7441065407a1597ab0 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Mon, 17 May 2021 22:27:44 +0800 Subject: [PATCH 08/12] the time of one coding, first or last session was not taken into account in the original algorithm --- src/main.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index da17396..b33b662 100644 --- a/src/main.rs +++ b/src/main.rs @@ -391,25 +391,35 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com commits.sort_by(|a, b| a.time().cmp(&b.time())); + //let lalala = Utc.timestamp(commits[commits.len() - 3].time().seconds(), 0).format("%Y-%m-%d"); + //let lilili = Utc.timestamp(commits[commits.len() - 2].time().seconds(), 0).format("%Y-%m-%d"); + //println!("just checking two last commits"); + //println!("{}", lalala); + //println!("{}", lilili); + let mut coding_session_start = Utc.timestamp(commits[0].time().seconds(), 0).format("%Y-%m-%d"); let len = commits.len() - 1; let all_but_last = commits.iter().enumerate().take(len); - let breakdown = all_but_last.fold(HashMap::new(), |mut breakdown, (i, commit)| { + let mut breakdown = HashMap::new(); + breakdown.entry(coding_session_start.to_string()) + .or_insert_with(|| *first_commit_addition); + + for (i, commit) in all_but_last { let next_commit = commits.get(i+1).unwrap(); let diff_seconds = next_commit.time().seconds() - commit.time().seconds(); let dur = Duration::seconds(diff_seconds); + if dur < *max_commit_diff { breakdown.entry(coding_session_start.to_string()) .and_modify(|e| { *e = *e + dur }) .or_insert_with(|| dur); } else { - coding_session_start = Utc.timestamp(commit.time().seconds(), 0).format("%Y-%m-%d"); + coding_session_start = Utc.timestamp(next_commit.time().seconds(), 0).format("%Y-%m-%d"); breakdown.entry(coding_session_start.to_string()) .and_modify(|e| { *e = *e + *first_commit_addition }) .or_insert_with(|| *first_commit_addition); } - breakdown - }); + }; let duration = breakdown.values().fold(Duration::minutes(0), |acc, dur| { acc + *dur From cdea7f4f2b4217400a4948e7f7d2c3bfc5783eb9 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Wed, 19 May 2021 16:14:44 +0800 Subject: [PATCH 09/12] removing commented out debug print statements --- src/main.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index b33b662..22121f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -391,12 +391,6 @@ fn estimate_author_time(mut commits: Vec, email: Option, max_com commits.sort_by(|a, b| a.time().cmp(&b.time())); - //let lalala = Utc.timestamp(commits[commits.len() - 3].time().seconds(), 0).format("%Y-%m-%d"); - //let lilili = Utc.timestamp(commits[commits.len() - 2].time().seconds(), 0).format("%Y-%m-%d"); - //println!("just checking two last commits"); - //println!("{}", lalala); - //println!("{}", lilili); - let mut coding_session_start = Utc.timestamp(commits[0].time().seconds(), 0).format("%Y-%m-%d"); let len = commits.len() - 1; let all_but_last = commits.iter().enumerate().take(len); From 12eec2776d4b7ef969e4a1784d346d2059c565c6 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Wed, 19 May 2021 16:16:07 +0800 Subject: [PATCH 10/12] removing unused parameters --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 22121f2..92eefee 100644 --- a/src/main.rs +++ b/src/main.rs @@ -386,7 +386,7 @@ struct CommitHours { commit_count: usize } -fn estimate_author_time(mut commits: Vec, email: Option, max_commit_diff: &Duration, first_commit_addition: &Duration, display_breakdown: &bool) -> CommitHours { +fn estimate_author_time(mut commits: Vec, email: Option, max_commit_diff: &Duration, first_commit_addition: &Duration) -> CommitHours { let author_name = commits[0].author().name().map(|n| n.to_string()); commits.sort_by(|a, b| a.time().cmp(&b.time())); @@ -458,11 +458,11 @@ fn estimate_author_times(config: &Config, commits: Vec) -> Vec 0 { - result.push(estimate_author_time(no_email, None, &config.max_commit_diff, &config.first_commit_addition, &config.display_breakdown)); + result.push(estimate_author_time(no_email, None, &config.max_commit_diff, &config.first_commit_addition)); } for (email, author_commits) in by_email { - result.push(estimate_author_time(author_commits, Some(email), &config.max_commit_diff, &config.first_commit_addition, &config.display_breakdown)); + result.push(estimate_author_time(author_commits, Some(email), &config.max_commit_diff, &config.first_commit_addition)); } result.sort_by(|a, b| { From 86664c50b2811738b2f690feaca9f4803eda8203 Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Fri, 20 Aug 2021 14:55:05 +0800 Subject: [PATCH 11/12] not including breakdown json field when breakdown flag is absent --- src/main.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 92eefee..1972313 100644 --- a/src/main.rs +++ b/src/main.rs @@ -578,7 +578,7 @@ fn print_breakdown_results_stdout(times: &Vec) -> Result<()> { } #[derive(Clone, Serialize, Deserialize)] -struct CommitHoursJson { +struct CommitBreakdownHoursJson { email: Option, author_name: Option, breakdown: Option>, @@ -586,13 +586,13 @@ struct CommitHoursJson { commit_count: usize } -impl From<&CommitHours> for CommitHoursJson { +impl From<&CommitHours> for CommitBreakdownHoursJson { fn from(time: &CommitHours) -> Self { let mut breakdown = HashMap::new(); for (key, value) in &(time.breakdown) { breakdown.insert(key.to_owned(), value.num_minutes() as f32 / 60.0); } - CommitHoursJson { + CommitBreakdownHoursJson { email: time.email.clone(), author_name: time.author_name.clone(), breakdown: Some(breakdown), @@ -602,6 +602,44 @@ impl From<&CommitHours> for CommitHoursJson { } } +#[derive(Clone, Serialize, Deserialize)] +struct CommitHoursJson { + email: Option, + author_name: Option, + hours: f32, + commit_count: usize +} + +impl From<&CommitHours> for CommitHoursJson { + fn from(time: &CommitHours) -> Self { + CommitHoursJson { + email: time.email.clone(), + author_name: time.author_name.clone(), + hours: time.duration.num_minutes() as f32 / 60.0, + commit_count: time.commit_count, + } + } +} + +fn print_breakdown_results_json(times: &Vec) -> Result<()> { + let mut times_json = times.iter().map(CommitBreakdownHoursJson::from).collect::>(); + + let (total_estimated_hours, total_commits) = get_totals(times); + times_json.push(CommitBreakdownHoursJson { + email: None, + author_name: Some(String::from("Total")), + breakdown: None, + hours: total_estimated_hours, + commit_count: total_commits + }); + + let json = serde_json::to_string_pretty(×_json)?; + + println!("{}", json); + + Ok(()) +} + fn print_results_json(times: &Vec) -> Result<()> { let mut times_json = times.iter().map(CommitHoursJson::from).collect::>(); @@ -609,7 +647,6 @@ fn print_results_json(times: &Vec) -> Result<()> { times_json.push(CommitHoursJson { email: None, author_name: Some(String::from("Total")), - breakdown: None, hours: total_estimated_hours, commit_count: total_commits }); @@ -627,7 +664,10 @@ fn print_results(times: &Vec, output_format: &OutputFormat, display true => print_breakdown_results_stdout(times), false => print_results_stdout(times) }, - OutputFormat::Json => print_results_json(times) + OutputFormat::Json => match *display_breakdown { + true => print_breakdown_results_json(times), + false => print_results_json(times) + } } } From 7f63821c08ec28edb349ea5415d803cef11c45ad Mon Sep 17 00:00:00 2001 From: marekyggdrasil Date: Thu, 23 Sep 2021 18:04:33 +0800 Subject: [PATCH 12/12] managed to get rid of type distinction between structures with and without breakdown --- src/main.rs | 131 +++++++++++----------------------------------------- 1 file changed, 28 insertions(+), 103 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1972313..9e069ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -489,7 +489,7 @@ fn get_totals(times: &Vec) -> (f32, usize) { (total_estimated_hours, total_commits) } -fn print_results_stdout(times: &Vec) -> Result<()> { +fn print_results_stdout(times: &Vec, display_breakdown: &bool) -> Result<()> { let mut table = Table::new(); let format = format::FormatBuilder::new() @@ -501,49 +501,12 @@ fn print_results_stdout(times: &Vec) -> Result<()> { .padding(1, 1) .build(); table.set_format(format); - - table.set_titles(row!["Author", "Email", "Commits", "Estimated Hours"]); - table.add_empty_row(); - - for time in times.iter() { - let author = match &time.author_name { - Some(n) => n, - None => "" - }; - let email = match &time.email { - Some(email) => email, - None => "(none)" - }; - let commits = time.commit_count; - let estimated_hours = (time.duration.num_minutes() as f32) / 60.0; - - table.add_row(row![author, email, commits, estimated_hours]); + if *display_breakdown { + table.set_titles(row!["Author", "Email", "Commits", "Date", "Estimated Hours"]); + } else { + table.set_titles(row!["Author", "Email", "Commits", "Estimated Hours"]); } - - table.add_empty_row(); - - let (total_estimated_hours, total_commits) = get_totals(times); - table.add_row(row!["Total", "", total_commits, total_estimated_hours]); - - table.printstd(); - - Ok(()) -} - -fn print_breakdown_results_stdout(times: &Vec) -> Result<()> { - let mut table = Table::new(); - - let format = format::FormatBuilder::new() - .column_separator('|') - .borders('|') - .separators(&[format::LinePosition::Top, - format::LinePosition::Bottom], - format::LineSeparator::new('-', '+', '+', '+')) - .padding(1, 1) - .build(); - table.set_format(format); - - table.set_titles(row!["Author", "Email", "Commits", "Date", "Estimated Hours"]); + ; table.add_empty_row(); for time in times.iter() { @@ -558,44 +521,42 @@ fn print_breakdown_results_stdout(times: &Vec) -> Result<()> { let commits = time.commit_count; let estimated_total_hours = (time.duration.num_minutes() as f32) / 60.0; - for (date, duration) in &(time.breakdown) { - let date = date.to_owned(); - let work_time = duration.num_minutes() as f32 / 60.0; - table.add_row(row!["", "", "", date, work_time]); + if *display_breakdown { + for (date, duration) in &(time.breakdown) { + let date = date.to_owned(); + let work_time = duration.num_minutes() as f32 / 60.0; + table.add_row(row!["", "", "", date, work_time]); + } + table.add_row(row![author, email, commits, "Total", estimated_total_hours]); + table.add_empty_row(); + } else { + table.add_row(row![author, email, commits, estimated_total_hours]); } - table.add_row(row![author, email, commits, "Total", estimated_total_hours]); - table.add_empty_row(); } table.add_empty_row(); let (total_estimated_hours, total_commits) = get_totals(times); - table.add_row(row!["Total", "", total_commits, "", total_estimated_hours]); + table.add_row(row!["Total", "", total_commits, total_estimated_hours]); table.printstd(); Ok(()) } -#[derive(Clone, Serialize, Deserialize)] -struct CommitBreakdownHoursJson { - email: Option, - author_name: Option, - breakdown: Option>, - hours: f32, - commit_count: usize -} - -impl From<&CommitHours> for CommitBreakdownHoursJson { +impl From<&CommitHours> for CommitHoursJson { fn from(time: &CommitHours) -> Self { + // how to apply this section of code conditionally + // only if time.breakdown is defined? let mut breakdown = HashMap::new(); - for (key, value) in &(time.breakdown) { + for (key, value) in &time.breakdown { breakdown.insert(key.to_owned(), value.num_minutes() as f32 / 60.0); } - CommitBreakdownHoursJson { + // end of relevant section + CommitHoursJson { email: time.email.clone(), author_name: time.author_name.clone(), - breakdown: Some(breakdown), + breakdown: Some(breakdown), // how to not include that field when not relvant? hours: time.duration.num_minutes() as f32 / 60.0, commit_count: time.commit_count, } @@ -606,40 +567,11 @@ impl From<&CommitHours> for CommitBreakdownHoursJson { struct CommitHoursJson { email: Option, author_name: Option, + #[serde(skip_serializing_if = "Option::is_none")] breakdown: Option>, hours: f32, commit_count: usize } -impl From<&CommitHours> for CommitHoursJson { - fn from(time: &CommitHours) -> Self { - CommitHoursJson { - email: time.email.clone(), - author_name: time.author_name.clone(), - hours: time.duration.num_minutes() as f32 / 60.0, - commit_count: time.commit_count, - } - } -} - -fn print_breakdown_results_json(times: &Vec) -> Result<()> { - let mut times_json = times.iter().map(CommitBreakdownHoursJson::from).collect::>(); - - let (total_estimated_hours, total_commits) = get_totals(times); - times_json.push(CommitBreakdownHoursJson { - email: None, - author_name: Some(String::from("Total")), - breakdown: None, - hours: total_estimated_hours, - commit_count: total_commits - }); - - let json = serde_json::to_string_pretty(×_json)?; - - println!("{}", json); - - Ok(()) -} - fn print_results_json(times: &Vec) -> Result<()> { let mut times_json = times.iter().map(CommitHoursJson::from).collect::>(); @@ -647,12 +579,11 @@ fn print_results_json(times: &Vec) -> Result<()> { times_json.push(CommitHoursJson { email: None, author_name: Some(String::from("Total")), + breakdown: None, hours: total_estimated_hours, commit_count: total_commits }); - let json = serde_json::to_string_pretty(×_json)?; - println!("{}", json); Ok(()) @@ -660,14 +591,8 @@ fn print_results_json(times: &Vec) -> Result<()> { fn print_results(times: &Vec, output_format: &OutputFormat, display_breakdown: &bool) -> Result<()> { match output_format { - OutputFormat::Stdout => match *display_breakdown { - true => print_breakdown_results_stdout(times), - false => print_results_stdout(times) - }, - OutputFormat::Json => match *display_breakdown { - true => print_breakdown_results_json(times), - false => print_results_json(times) - } + OutputFormat::Stdout => print_results_stdout(times, display_breakdown), + OutputFormat::Json => print_results_json(times) } }