1#![deny(clippy::implicit_return)]
2#![allow(clippy::needless_return)]
3use rusqlite::{ Connection, Result };
4use std::process;
6use chrono::*;
7use inline_colorization::*;
8use comfy_table::*;
9
10pub struct Config {
11 pub path: String,
12 pub analysis: String,
13}
14impl Config {
15 pub fn new(args: &[String]) -> Result<Config> {
16 if args.len() < 3 {
17 eprintln!(
18 "{color_red}{style_bold}Not enough arguments: {style_reset}{color_reset}nu-hist [path to history.sqlite3 file] [year as number | 'all']"
19 );
20 process::exit(1);
21 }
22 let path = args[1].clone();
23 let analysis = args[2].clone();
24 return Ok(Config { path, analysis });
25 }
26}
27
28pub fn year(conn: Connection, year: String) -> Result<()> {
29 let year: i32 = year.to_string().parse::<i32>().unwrap();
30 let (start, end) = year_to_unix(year);
31
32 let mut content: rusqlite::Statement<'_> = conn.prepare(
33 "select * from history where start_timestamp >= ?1 AND start_timestamp <= ?2"
34 )?;
35 let mut rows = content.query([start, end])?;
36
37 let mut arr: Vec<String> = Vec::new();
38 while let Some(row) = rows.next()? {
39 let command: String = row.get(1)?;
40 arr.push(command);
41 }
42
43 let _ = top_ten_dur(&conn, start, end);
44 let mut head = Table::new();
45 head.set_header(
46 vec![
47 Cell::new("Year: ".to_string() + &year.to_string())
48 .add_attribute(Attribute::Bold)
49 .fg(Color::Magenta),
50 Cell::new("Total Commands: ".to_string() + &arr.len().to_string())
51 .add_attribute(Attribute::Bold)
52 .fg(Color::Magenta)
53 ]
54 );
55 println!("{}", head);
56 println!("{}", table(top_ten_dur(&conn, start, end).unwrap(), arr.len() as i64));
57 return Ok(());
58}
59
60pub fn all(conn: Connection) -> Result<()> {
61 let mut content: rusqlite::Statement<'_> = conn.prepare("SELECT COUNT(*) from history")?;
62 let mut rows = content.query([])?;
63 if let Some(row) = rows.next()? {
64 let len: i64 = row.get(0)?;
65 let mut head = Table::new();
66 head.set_header(
67 vec![
68 Cell::new("Total Commands: ".to_string() + &len.to_string())
69 .add_attribute(Attribute::Bold)
70 .fg(Color::Magenta)
71 ]
72 );
73 println!("{}", head);
74 println!("{}", table(top_ten_dur(&conn, 0, i64::MAX).unwrap(), len));
75 }
76 return Ok(());
77}
78
79fn top_ten_dur(conn: &Connection, start: i64, end: i64) -> Result<Vec<(String, String)>> {
80 let mut content: rusqlite::Statement<'_> = conn.prepare(
81 "SELECT
82 CASE WHEN instr(command_line, ' ') > 0
83 THEN substr(command_line, 1, instr(command_line, ' ') - 1)
84 ELSE command_line
85 END AS first_word,
86 CAST(count(*) AS VARCHAR) AS count
87 FROM history
88 WHERE start_timestamp >= ?1 AND start_timestamp <= ?2
89 GROUP BY first_word
90 ORDER BY CAST(count(*) AS INTEGER) DESC
91 LIMIT 10;"
92 )?;
93 let mut rows = content.query([start, end])?;
94 let mut arr: Vec<(String, String)> = Vec::new();
95 while let Some(row) = rows.next()? {
96 let a: String = row.get(0)?;
97 let b: String = row.get(1)?;
98 arr.push((a, b));
99 }
100 return Ok(arr);
101}
102fn table(arr: Vec<(String, String)>, total: i64) -> Table {
103 let mut table = Table::new();
104 table.set_header(
105 vec![
106 Cell::new("#").add_attribute(Attribute::Bold).fg(Color::Green),
107 Cell::new("Command").add_attribute(Attribute::Bold).fg(Color::Green),
108 Cell::new("Count").add_attribute(Attribute::Bold).fg(Color::Green),
109 Cell::new("Bar").add_attribute(Attribute::Bold).fg(Color::Green)
110 ]
111 );
112 let mut i = 0;
113 for (a, b) in arr {
114 let mut x: String = String::new();
115 for _ in 0..(b.parse::<i64>().unwrap() * 100) / total {
116 x = x + "■";
117 }
118 table.add_row(
119 vec![
120 Cell::new(&(i+1).to_string()).fg(color_by_index(i % 6)),
121 Cell::new(&a).fg(color_by_index(i % 6)),
122 Cell::new(&b).fg(color_by_index(i % 6)),
123 Cell::new(&x).fg(color_by_index(i % 6))
124 ]
125 );
126 i = i + 1;
127 }
128 return table;
129}
130fn color_by_index(index: usize) -> Color {
131 match index {
132 0 => Color::Red,
133 1 => Color::Green,
134 2 => Color::Yellow,
135 3 => Color::Blue,
136 4 => Color::Magenta,
137 5 => Color::Cyan,
138 6 => Color::White,
139 _ => Color::Reset,
140 }
141}
142
143fn year_to_unix(year: i32) -> (i64, i64) {
144 let start = Utc.with_ymd_and_hms(year, 1, 1, 00, 00, 00).unwrap().timestamp();
145 let end = Utc.with_ymd_and_hms(year, 12, 31, 23, 59, 59).unwrap().timestamp();
146 return (start * 1000, end * 1000);
147}
148
149