From c05d2850420a2c163b8f62c33a6cef7c0ae1ad8d Mon Sep 17 00:00:00 2001 From: Vladislav Stepanov <8uk.8ak@gmail.com> Date: Fri, 14 Apr 2023 23:18:58 +0400 Subject: Workspace reorder (#868) * Try different workspace structure Move main crate (atuin) to be on the same level with other crates in this workspace * extract common dependencies to the workspace definition * fix base64 v0.21 deprecation warning * questionable: update deps & fix chrono deprecations possible panic sites are unchanged, they're just more visible now * Revert "questionable: update deps & fix chrono deprecations" This reverts commit 993e60f8dea81a1625a04285a617959ad09a0866. --- src/command/client/stats.rs | 181 -------------------------------------------- 1 file changed, 181 deletions(-) delete mode 100644 src/command/client/stats.rs (limited to 'src/command/client/stats.rs') diff --git a/src/command/client/stats.rs b/src/command/client/stats.rs deleted file mode 100644 index 5134f22f..00000000 --- a/src/command/client/stats.rs +++ /dev/null @@ -1,181 +0,0 @@ -use std::collections::{HashMap, HashSet}; - -use chrono::{prelude::*, Duration}; -use clap::Parser; -use crossterm::style::{Color, ResetColor, SetAttribute, SetForegroundColor}; -use eyre::{bail, Result}; -use interim::parse_date_string; - -use atuin_client::{ - database::{current_context, Database}, - history::History, - settings::{FilterMode, Settings}, -}; - -#[derive(Parser)] -#[command(infer_subcommands = true)] -pub struct Cmd { - /// compute statistics for the specified period, leave blank for statistics since the beginning - period: Vec, - - /// How many top commands to list - #[arg(long, short, default_value = "10")] - count: usize, -} - -fn compute_stats(history: &[History], count: usize) -> Result<()> { - let mut commands = HashSet::<&str>::with_capacity(history.len()); - let mut prefixes = HashMap::<&str, usize>::with_capacity(history.len()); - for i in history { - // just in case it somehow has a leading tab or space or something (legacy atuin didn't ignore space prefixes) - let command = i.command.trim(); - commands.insert(command); - *prefixes.entry(interesting_command(command)).or_default() += 1; - } - - let unique = commands.len(); - let mut top = prefixes.into_iter().collect::>(); - top.sort_unstable_by_key(|x| std::cmp::Reverse(x.1)); - top.truncate(count); - if top.is_empty() { - bail!("No commands found"); - } - - let max = top.iter().map(|x| x.1).max().unwrap(); - let num_pad = max.ilog10() as usize + 1; - - for (command, count) in top { - let gray = SetForegroundColor(Color::Grey); - let bold = SetAttribute(crossterm::style::Attribute::Bold); - - let in_ten = 10 * count / max; - print!("["); - print!("{}", SetForegroundColor(Color::Red)); - for i in 0..in_ten { - if i == 2 { - print!("{}", SetForegroundColor(Color::Yellow)); - } - if i == 5 { - print!("{}", SetForegroundColor(Color::Green)); - } - print!("▮"); - } - for _ in in_ten..10 { - print!(" "); - } - - println!("{ResetColor}] {gray}{count:num_pad$}{ResetColor} {bold}{command}{ResetColor}"); - } - println!("Total commands: {}", history.len()); - println!("Unique commands: {unique}"); - - Ok(()) -} - -impl Cmd { - pub async fn run(&self, db: &mut impl Database, settings: &Settings) -> Result<()> { - let context = current_context(); - let words = if self.period.is_empty() { - String::from("all") - } else { - self.period.join(" ") - }; - let history = if words.as_str() == "all" { - db.list(FilterMode::Global, &context, None, false).await? - } else if words.trim() == "today" { - let start = Local::now().date().and_hms(0, 0, 0); - let end = start + Duration::days(1); - db.range(start.into(), end.into()).await? - } else if words.trim() == "month" { - let end = Local::now().date().and_hms(0, 0, 0); - let start = end - Duration::days(31); - db.range(start.into(), end.into()).await? - } else if words.trim() == "week" { - let end = Local::now().date().and_hms(0, 0, 0); - let start = end - Duration::days(7); - db.range(start.into(), end.into()).await? - } else if words.trim() == "year" { - let end = Local::now().date().and_hms(0, 0, 0); - let start = end - Duration::days(365); - db.range(start.into(), end.into()).await? - } else { - let start = parse_date_string(&words, Local::now(), settings.dialect.into())?; - let end = start + Duration::days(1); - db.range(start.into(), end.into()).await? - }; - compute_stats(&history, self.count)?; - Ok(()) - } -} - -// TODO: make this configurable? -static COMMON_COMMAND_PREFIX: &[&str] = &["sudo"]; -static COMMON_SUBCOMMAND_PREFIX: &[&str] = &["cargo", "go", "git", "npm", "yarn", "pnpm"]; - -fn first_non_whitespace(s: &str) -> Option { - s.char_indices() - // find the first non whitespace char - .find(|(_, c)| !c.is_ascii_whitespace()) - // return the index of that char - .map(|(i, _)| i) -} - -fn first_whitespace(s: &str) -> usize { - s.char_indices() - // find the first whitespace char - .find(|(_, c)| c.is_ascii_whitespace()) - // return the index of that char, (or the max length of the string) - .map_or(s.len(), |(i, _)| i) -} - -fn interesting_command(mut command: &str) -> &str { - // compute command prefix - // we loop here because we might be working with a common command prefix (eg sudo) that we want to trim off - let (i, prefix) = loop { - let i = first_whitespace(command); - let prefix = &command[..i]; - - // is it a common prefix - if COMMON_COMMAND_PREFIX.contains(&prefix) { - command = command[i..].trim_start(); - if command.is_empty() { - // no commands following, just use the prefix - return prefix; - } - } else { - break (i, prefix); - } - }; - - // compute subcommand - let subcommand_indices = command - // after the end of the command prefix - .get(i..) - // find the first non whitespace character (start of subcommand) - .and_then(first_non_whitespace) - // then find the end of that subcommand - .map(|j| i + j + first_whitespace(&command[i + j..])); - - match subcommand_indices { - // if there is a subcommand and it's a common one, then count the full prefix + subcommand - Some(end) if COMMON_SUBCOMMAND_PREFIX.contains(&prefix) => &command[..end], - // otherwise just count the main command - _ => prefix, - } -} - -#[cfg(test)] -mod tests { - use super::interesting_command; - - #[test] - fn interesting_commands() { - assert_eq!(interesting_command("cargo"), "cargo"); - assert_eq!(interesting_command("cargo build foo bar"), "cargo build"); - assert_eq!( - interesting_command("sudo cargo build foo bar"), - "cargo build" - ); - assert_eq!(interesting_command("sudo"), "sudo"); - } -} -- cgit v1.3.1