diff --git a/src/commands.rs b/src/commands.rs index bef4958..a29bdd4 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -143,12 +143,37 @@ impl Times { } } +/// Classify emerge commands by looking at their args. +/// +/// Note that some commands don't get logged at all, so this enum is quite limited. +#[derive(PartialEq, Eq, PartialOrd, Ord)] +enum ArgKind { + All, + Merge, + Clean, + Sync, +} +impl ArgKind { + fn new(args: &str) -> Self { + for arg in args.split_ascii_whitespace() { + match arg { + "--deselect" | "--unmerge" | "--clean" | "--depclean" => return Self::Clean, + "--sync" => return Self::Sync, + _ => (), + } + } + Self::Merge + } +} + /// Summary display of merge events /// /// First loop is like cmd_list but we store the merge time for each ebuild instead of printing it. /// Then we compute the stats per ebuild, and print that. pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result { let hist = get_hist(&gc.logfile, gc.from, gc.to, sc.show, &sc.search, sc.exact)?; + let h = [sc.group.name(), "Logged emerges", "Install/Update", "Unmerge/Clean", "Sync"]; + let mut tblc = Table::new(&gc).margin(1, " ").header(h); let h = [sc.group.name(), "Repo", "Syncs", "Total time", "Predict time"]; let mut tbls = Table::new(&gc).align_left(0).align_left(1).margin(1, " ").header(h); let h = [sc.group.name(), @@ -173,6 +198,7 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result { let mut pkg_time: BTreeMap = BTreeMap::new(); let mut sync_start: Option = None; let mut sync_time: BTreeMap = BTreeMap::new(); + let mut cmd_args: BTreeMap = BTreeMap::new(); let mut nextts = 0; let mut curts = 0; for p in hist { @@ -183,16 +209,20 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result { curts = t; } else if t > nextts { let group = sc.group.at(curts, gc.date_offset); - cmd_stats_group(&gc, &sc, &mut tbls, &mut tblp, &mut tblt, group, &sync_time, - &pkg_time); + cmd_stats_group(&gc, &sc, &mut tblc, &mut tbls, &mut tblp, &mut tblt, group, + &cmd_args, &sync_time, &pkg_time); sync_time.clear(); pkg_time.clear(); + cmd_args.clear(); nextts = sc.group.next(t, gc.date_offset); curts = t; } } match p { - Hist::CmdStart { .. } => todo!("CmdStart"), + Hist::CmdStart { args, .. } => { + *cmd_args.entry(ArgKind::All).or_insert(0) += 1; + *cmd_args.entry(ArgKind::new(&args)).or_insert(0) += 1; + }, Hist::MergeStart { ts, key, .. } => { merge_start.insert(key, ts); }, @@ -228,15 +258,20 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result { } } let group = sc.group.at(curts, gc.date_offset); - cmd_stats_group(&gc, &sc, &mut tbls, &mut tblp, &mut tblt, group, &sync_time, &pkg_time); + cmd_stats_group(&gc, &sc, &mut tblc, &mut tbls, &mut tblp, &mut tblt, group, &cmd_args, + &sync_time, &pkg_time); // Controlled drop to ensure table order and insert blank lines - let (es, ep, et) = (!tbls.is_empty(), !tblp.is_empty(), !tblt.is_empty()); + let (ec, es, ep, et) = (!tblc.is_empty(), !tbls.is_empty(), !tblp.is_empty(), !tblt.is_empty()); + drop(tblc); + if ec && es { + println!(); + } drop(tbls); - if es && ep { + if (ec || es) && ep { println!(); } drop(tblp); - if (es || ep) && et { + if (ec || es || ep) && et { println!(); } drop(tblt); @@ -247,12 +282,22 @@ pub fn cmd_stats(gc: Conf, sc: ConfStats) -> Result { #[allow(clippy::too_many_arguments)] fn cmd_stats_group(gc: &Conf, sc: &ConfStats, + tblc: &mut Table<5>, tbls: &mut Table<5>, tblp: &mut Table<8>, tblt: &mut Table<7>, group: String, + cmd_args: &BTreeMap, sync_time: &BTreeMap, pkg_time: &BTreeMap) { + // Commands + if sc.show.cmd && !cmd_args.is_empty() { + tblc.row([&[&group], + &[&gc.cnt, cmd_args.get(&ArgKind::All).unwrap_or(&0)], + &[&gc.cnt, cmd_args.get(&ArgKind::Merge).unwrap_or(&0)], + &[&gc.cnt, cmd_args.get(&ArgKind::Clean).unwrap_or(&0)], + &[&gc.cnt, cmd_args.get(&ArgKind::Sync).unwrap_or(&0)]]); + } // Syncs if sc.show.sync && !sync_time.is_empty() { for (repo, time) in sync_time { diff --git a/src/config.rs b/src/config.rs index 428716f..4fd4740 100644 --- a/src/config.rs +++ b/src/config.rs @@ -220,7 +220,7 @@ impl ConfPred { impl ConfStats { fn try_new(cli: &ArgMatches, toml: &Toml) -> Result { - Ok(Self { show: sel!(cli, toml, stats, show, "ptsa", Show::p())?, + Ok(Self { show: sel!(cli, toml, stats, show, "cptsa", Show::p())?, search: cli.get_many("search").unwrap_or_default().cloned().collect(), exact: cli.get_flag("exact"), lim: sel!(cli, toml, stats, limit, 1..=65000, 10)? as u16, diff --git a/src/config/cli.rs b/src/config/cli.rs index d419761..ce5bf61 100644 --- a/src/config/cli.rs +++ b/src/config/cli.rs @@ -43,17 +43,19 @@ pub fn build_cli() -> Command { u: Package unmerges\n \ s: Repository syncs\n \ a: All of the above"); - let show_s = Arg::new("show").short('s') - .long("show") - .value_name("p,t,s,a") - .display_order(3) - .help_heading("Filter") - .help("Show (p)ackages, (t)otals, (s)yncs, and/or (a)ll") - .long_help("Show (any combination of)\n \ - p: Individual package merges/unmerges\n \ - t: Total package merges/unmerges\n \ - s: Repository syncs\n \ - a: All of the above"); + let show_s = + Arg::new("show").short('s') + .long("show") + .value_name("c,p,t,s,a") + .display_order(3) + .help_heading("Filter") + .help("Show (c)commands, (p)ackages, (t)otals, (s)yncs, and/or (a)ll") + .long_help("Show (any combination of)\n \ + c: Emerge commands\n \ + p: Individual package merges/unmerges\n \ + t: Total package merges/unmerges\n \ + s: Repository syncs\n \ + a: All of the above"); let show_p = Arg::new("show").short('s') .long("show") .value_name("e,m,t,a") diff --git a/src/parse/history.rs b/src/parse/history.rs index d925aec..5b039b9 100644 --- a/src/parse/history.rs +++ b/src/parse/history.rs @@ -443,17 +443,20 @@ mod tests { } #[test] - /// Basic counts, with every combination of merge/unmerge/sync + /// Basic counts, with every combination of command/merge/unmerge/sync fn parse_hist_nofilter() { - for i in 0..8 { - let m = (i & 0b001) == 0; - let u = (i & 0b010) == 0; - let s = (i & 0b100) == 0; - let show = format!("{}{}{}", + for i in 0..16 { + let c = (i & 0b0001) == 0; + let m = (i & 0b0010) == 0; + let u = (i & 0b0100) == 0; + let s = (i & 0b1000) == 0; + let show = format!("{}{}{}{}", + if c { "c" } else { "" }, if m { "m" } else { "" }, if u { "u" } else { "" }, if s { "s" } else { "" }); - let t = vec![("MStart", if m { 889 } else { 0 }), + let t = vec![("CStart", if c { 450 } else { 0 }), + ("MStart", if m { 889 } else { 0 }), ("MStop", if m { 832 } else { 0 }), ("UStart", if u { 832 } else { 0 }), ("UStop", if u { 832 } else { 0 }), diff --git a/tests/commands.rs b/tests/commands.rs index e3da506..daca056 100644 --- a/tests/commands.rs +++ b/tests/commands.rs @@ -292,7 +292,9 @@ fn stats() { 0), ("%F10000.log s client -sst -oc", "11 24:00:24 2:10:56 10 27 2\n", 0), ("%F10000.log s client -sa -oc", - "kde-frameworks/kxmlrpcclient 2 47 23 2 4 2\n\ + "450 267 20 163\n\ + \n\ + kde-frameworks/kxmlrpcclient 2 47 23 2 4 2\n\ mail-client/thunderbird 2 1:23:44 41:52 2 6 3\n\ www-client/chromium 3 21:41:24 7:42:07 3 12 3\n\ www-client/falkon 1 6:02 6:02 0 0 ?\n\ @@ -494,7 +496,7 @@ fn negative_merge_time() { 2019-06-05 10:21:02 ? >>> kde-plasma/kwin-5.15.5\n\ 2019-06-08 21:33:36 3:10 >>> kde-plasma/kwin-5.15.5\n")), // For `stats` the negative merge time is used for count but ignored for tottime/predtime. - ("%Fnegtime.log s -sa -oc", + ("%Fnegtime.log s -sstp -oc", format!("gentoo 2 1:06 1:06\n\ \n\ kde-apps/libktnef 1 26 26 0 0 ?\n\