mirror of
https://github.com/nthnd/muc.git
synced 2026-05-06 03:00:27 -04:00
refactor: rethink how the bar arg works
This commit is contained in:
68
src/args.rs
68
src/args.rs
@@ -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
82
src/display.rs
Normal 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}",
|
||||
);
|
||||
}
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
92
src/utils.rs
92
src/utils.rs
@@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user