refactor: rethink how the bar arg works

This commit is contained in:
nathan dawit
2023-03-14 15:01:42 +03:00
parent ccc96ab798
commit a4a61fac82
5 changed files with 136 additions and 120 deletions

View File

@@ -1,31 +1,23 @@
use std::str::FromStr;
// use utf8_slice;
use clap::Parser;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
/// Display top n commands
#[arg(short, long)]
pub count: Option<usize>,
/// Make output pretty
#[arg(short, long, default_value_t = true)]
pub pretty: bool,
#[arg(short, long, default_value_t = 10)]
pub count: usize,
/// Show debug messages
#[arg(long, default_value_t = false)]
pub debug: bool,
/// Bar opening character
#[arg(long, default_value_t = '[')]
pub bar_open: char,
/// Bar closing character
#[arg(long, default_value_t = ']')]
pub bar_close: char,
/// Bar character
#[arg(long, default_value_t = '▮')]
pub bar: char,
/// Change how the bar looks --bar [,▮, ,]
#[arg(long, default_value_t = Default::default())]
pub bar: Bar,
/// Regular expression to allow for the removal of prefixes in shells like zsh. Default value is for zsh. NOTE: shell overrides this argument
#[arg(short, long, default_value_t = String::from(""))]
@@ -34,5 +26,45 @@ pub struct Args {
/// Preset regular expressions for common shells: Bash, ZSH, Fish.
#[arg(long, default_value_t = String::from(""))]
pub shell: String,
}
#[derive(Debug, Clone)]
pub struct Bar {
pub opening: String,
pub closing: String,
pub fill: String,
pub empty: String,
}
impl Default for Bar {
fn default() -> Self {
Bar {
opening: "[".to_owned(),
fill: "".to_owned(),
empty: " ".to_owned(),
closing: "]".to_owned(),
}
}
}
impl FromStr for Bar {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let chars = s.split(',').collect::<Vec<&str>>();
match chars.len() {
4 => Ok(Bar {
opening: chars.get(0).unwrap().to_string(),
fill: chars.get(1).unwrap().to_string(),
empty: chars.get(2).unwrap().to_string(),
closing: chars.get(3).unwrap().to_string(),
}),
_ => Err("Invalid bar length".to_string()),
}
}
}
impl std::fmt::Display for Bar {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{},{},{},{}", self.opening, self.fill, self.empty, self.closing)
}
}

82
src/display.rs Normal file
View File

@@ -0,0 +1,82 @@
use std::{
collections::{BTreeMap, HashMap},
usize,
};
use utf8_slice::slice;
use crate::{hist_file::CommandMap, Args};
use aecir::style::{Color, ColorName, Format};
type VeryComplexType = (String, Option<bool>, HashMap<String, usize>);
pub fn print(data: CommandMap, args: Args) {
let tree: BTreeMap<usize, VeryComplexType> = data
.into_iter()
.map(|(s, (f, o, h))| (f, (s, o, h)))
.collect();
let total = tree.len();
let max = *tree.last_key_value().unwrap().0;
let limited_tree: Vec<(usize, VeryComplexType)> = tree.into_iter().rev().collect();
for (freq, elem) in limited_tree[..(usize::min(args.count, limited_tree.len()))].iter() {
let (s, _o, h) = elem;
let mut sub_commands = h.iter().collect::<Vec<(&String, &usize)>>();
sub_commands.sort_by(|a, b| b.1.cmp(a.1));
let sub_commands = sub_commands[..(usize::min(3, sub_commands.len()))]
.iter()
.map(|x| x.0.to_owned())
.collect();
print_command(s, *freq, max, total, &args, Some(sub_commands));
}
}
pub fn print_command(
command: &str,
invocations: usize,
max: usize,
total: usize,
args: &Args,
sub_commands: Option<Vec<String>>,
) {
let percentage = invocations as f32 / total as f32;
let num_of_bars = ((invocations as f32 / max as f32) * 10.) as usize ;
let bars = args.bar.fill.repeat(num_of_bars);
let empties = args.bar.empty.repeat(10 - num_of_bars);
let bar: String = format!(
"{}{}",
bars, empties
);
let pretty_sub_commands = if let Some(sub_commands) = sub_commands {
let trim_len = sub_commands.len().min(3);
let mut x = sub_commands[..trim_len].join(", ");
x.push_str(" ...");
x
} else {
"".to_string()
};
let opening_char = &args.bar.opening;
let bar_first = slice(&bar, 0, 2);
let bar_second = slice(&bar, 2, 5);
let bar_third = slice(&bar, 5, 10);
let closing_char = &args.bar.closing;
let reset_style = aecir::style::reset_all();
let (red, yellow, green, gray, bold) = (
Color::Fg(ColorName::Red).to_string(),
Color::Fg(ColorName::Yellow).to_string(),
Color::Fg(ColorName::Green).to_string(),
Color::Fg(ColorName::LightBlack).to_string(),
Format::Bold.to_string(),
);
println!(
"{opening_char}{red}{bar_first: <2}{yellow}{bar_second: <3}{green}{bar_third: <5}{reset_style}{closing_char} \
{percentage: >5.2}% {gray}{invocations:5}{reset_style}\
{bold} {command} {reset_style}{gray}{pretty_sub_commands} {reset_style}",
);
}

View File

@@ -14,7 +14,6 @@ fn print_warning(warning: &str) {
}
pub fn get_contents(hist_file: std::fs::File, args: &Args) -> String {
let reader = BufReader::new(hist_file);
let mut contents = String::new();
@@ -69,11 +68,7 @@ pub fn parse_contents(contents: String, args: &Args) -> Vec<String> {
let reg = Regex::new("('(?:.|[^'\n])*'|\"(?:.|[^\"\n])*\")").unwrap();
let unquoted_lines = shell_parsed.map(|line| reg.replace_all(line, "").to_string());
let command_lines = unquoted_lines
.flat_map(get_commands)
.collect();
command_lines
unquoted_lines.flat_map(get_commands).collect()
}
pub(crate) type CommandMap = HashMap<String, (usize, Option<bool>, HashMap<String, usize>)>;
@@ -104,7 +99,7 @@ pub fn process_lines(lines: Vec<String>, _args: &Args) -> CommandMap {
if leaders.contains(&first.as_str()) {
parent_entry.1 = Some(true);
output
.entry(( *second ).to_string())
.entry((*second).to_string())
.or_insert((0, None, HashMap::new()))
.0 += 1;
} else if super_commands.contains(&first.as_str()) {

View File

@@ -1,5 +1,5 @@
mod hist_file;
mod utils;
mod display;
mod args;
use std::env;
@@ -17,6 +17,5 @@ fn main() {
let command_lines = hist_file::parse_contents(contents, &args);
let commands = hist_file::process_lines(command_lines, &args);
utils::display_sorted(commands, args);
display::print(commands, args);
}

View File

@@ -1,92 +0,0 @@
use std::{
collections::{BTreeMap, HashMap},
usize,
};
use utf8_slice::slice;
use crate::{hist_file::CommandMap, Args};
use aecir::style::{Color, ColorName, Format};
type VeryComplexType = (String, Option<bool>, HashMap<String, usize>);
pub fn display_sorted(data: CommandMap, args: Args) {
let tree: BTreeMap<usize, VeryComplexType> = data
.into_iter()
.map(|(s, (f, o, h))| (f, (s, o, h)))
.collect();
let total = tree.len();
let max = *tree.last_key_value().unwrap().0;
let limited_tree: Vec<(usize, VeryComplexType)> =
tree.into_iter().rev().collect();
for (freq, elem) in
limited_tree[..(usize::min(args.count.unwrap(), limited_tree.len()))].iter()
{
let (s, _o, h) = elem;
let mut sub_commands = h.iter().collect::<Vec<(&String, &usize)>>();
sub_commands.sort_by(|a, b| b.1.cmp(a.1));
let sub_commands = sub_commands[..(usize::min(3, sub_commands.len()))]
.iter()
.map(|x| x.0.to_owned())
.collect();
print_command(
s,
*freq,
*freq as f32 / total as f32,
max,
&args,
Some(sub_commands),
);
}
}
pub fn print_command(
command: &str,
invocations: usize,
percentage: f32,
max: usize,
args: &Args,
sub_commands: Option<Vec<String>>,
) {
let bar: String = format!(
"{: <10}",
args.bar
.to_string()
.repeat(((invocations as f32 / max as f32) * 10.) as usize)
);
let pretty_sub_commands = if let Some(sub_commands) = sub_commands {
let trim_len = sub_commands.len().min(3);
let mut x = sub_commands[..trim_len].join(", ");
x.push_str(" ...");
x
} else {
"".to_string()
};
if args.pretty {
println!(
"{opening_char}{red}{bar_first: <2}{yellow}{bar_second: <3}{green}{bar_third: <5}{reset}{closing_char} \
{percentage: >5.2}% {gray}{invocations:5}{reset}\
{bold} {command} {reset_style}{gray}{pretty_sub_commands} {reset}",
opening_char = args.bar_open,
red = Color::Fg(ColorName::Red),
bar_first = slice(&bar, 0, 2),
yellow = Color::Fg(ColorName::Yellow),
bar_second = slice(&bar, 2, 5),
green = Color::Fg(ColorName::Green),
bar_third = slice(&bar, 5, 10),
reset = aecir::style::reset_colors(),
closing_char = args.bar_close,
gray = Color::Fg(ColorName::LightBlack),
bold = Format::Bold,
reset_style = aecir::style::reset_all(),
);
} else {
println!(
"{opening_char}{bar}{closing_char} {percentage: >5.2}% {invocations:5} {command}",
opening_char = args.bar_open,
closing_char = args.bar_close,
);
}
}