Some clippy and refactoring.

This commit is contained in:
ClementTsang 2020-02-17 17:42:51 -05:00
parent c669b5337c
commit 4485d1b380
6 changed files with 156 additions and 189 deletions

View File

@ -15,16 +15,17 @@ name = "btm"
path = "src/main.rs"
[profile.release]
debug = 1
# debug = true
debug = 1
opt-level = 'z' # Optimize for size.
# opt-level = 3 # Optimize for speed.
lto = true
[dependencies]
chrono = "0.4.10"
clap = "2.33.0"
fern = "0.5.9"
futures-timer = "3.0.1"
futures-timer = "3.0.2"
futures = "0.3.4"
heim = "0.0.10"
log = "0.4.8"

View File

@ -28,11 +28,6 @@ pub enum ScrollDirection {
DOWN,
}
lazy_static! {
static ref BASE_REGEX: std::result::Result<regex::Regex, regex::Error> =
regex::Regex::new(".*");
}
/// AppScrollWidgetState deals with fields for a scrollable app's current state.
#[derive(Default)]
pub struct AppScrollWidgetState {
@ -64,9 +59,10 @@ impl Default for AppScrollState {
pub struct AppSearchState {
is_enabled: bool,
current_search_query: String,
current_regex: std::result::Result<regex::Regex, regex::Error>,
current_regex: Option<std::result::Result<regex::Regex, regex::Error>>,
current_cursor_position: usize,
pub is_invalid_or_blank_search: bool,
pub is_blank_search: bool,
pub is_invalid_search: bool,
}
impl Default for AppSearchState {
@ -74,13 +70,20 @@ impl Default for AppSearchState {
AppSearchState {
is_enabled: false,
current_search_query: String::default(),
current_regex: BASE_REGEX.clone(),
current_regex: None,
current_cursor_position: 0,
is_invalid_or_blank_search: true,
is_invalid_search: false,
is_blank_search: true,
}
}
}
impl AppSearchState {
pub fn is_invalid_or_blank_search(&self) -> bool {
self.is_blank_search || self.is_invalid_search
}
}
/// ProcessSearchState only deals with process' search's current settings and state.
pub struct ProcessSearchState {
pub search_state: AppSearchState,
@ -388,6 +391,7 @@ impl App {
!self.cpu_state.core_show_vec[curr_posn as usize];
}
}
WidgetPosition::Network => {}
_ => {}
}
}
@ -457,43 +461,44 @@ impl App {
}
pub fn update_regex(&mut self) {
self.process_search_state.search_state.current_regex = if self
if self
.process_search_state
.search_state
.current_search_query
.is_empty()
{
self.process_search_state
.search_state
.is_invalid_or_blank_search = true;
BASE_REGEX.clone()
self.process_search_state.search_state.is_invalid_search = false;
self.process_search_state.search_state.is_blank_search = true;
} else {
let mut final_regex_string = self
.process_search_state
.search_state
.current_search_query
.clone();
if !self.process_search_state.is_searching_with_regex {
final_regex_string = regex::escape(&final_regex_string);
}
let regex_string = &self.process_search_state.search_state.current_search_query;
let escaped_regex: String;
let final_regex_string = &format!(
"{}{}{}",
if self.process_search_state.is_searching_whole_word {
final_regex_string = format!("^{}$", final_regex_string);
}
"^{}$"
} else {
""
},
if self.process_search_state.is_ignoring_case {
final_regex_string = format!("(?i){}", final_regex_string);
"(?i){}"
} else {
""
},
if !self.process_search_state.is_searching_with_regex {
escaped_regex = regex::escape(regex_string);
&escaped_regex
} else {
regex_string
}
);
regex::Regex::new(&final_regex_string)
};
self.process_search_state
.search_state
.is_invalid_or_blank_search = self
.process_search_state
.search_state
.current_regex
.is_err();
self.process_search_state.search_state.is_blank_search = false;
let new_regex = regex::Regex::new(final_regex_string);
self.process_search_state.search_state.is_invalid_search = new_regex.is_err();
self.process_search_state.search_state.current_regex = Some(new_regex);
}
self.app_scroll_positions
.process_scroll_state
.previous_scroll_position = 0;
@ -585,13 +590,7 @@ impl App {
pub fn clear_search(&mut self) {
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
self.process_search_state
.search_state
.current_cursor_position = 0;
self.process_search_state.search_state.current_search_query = String::default();
self.process_search_state
.search_state
.is_invalid_or_blank_search = true;
self.process_search_state = ProcessSearchState::default();
self.update_process_gui = true;
}
}
@ -623,7 +622,9 @@ impl App {
}
}
pub fn get_current_regex_matcher(&self) -> &std::result::Result<regex::Regex, regex::Error> {
pub fn get_current_regex_matcher(
&self,
) -> &Option<std::result::Result<regex::Regex, regex::Error>> {
&self.process_search_state.search_state.current_regex
}

View File

@ -116,9 +116,10 @@ fn get_process_cpu_stats(pid: u32) -> std::io::Result<f64> {
/// Note that cpu_fraction should be represented WITHOUT the \times 100 factor!
fn linux_cpu_usage<S: core::hash::BuildHasher>(
pid: u32, cpu_usage: f64, cpu_fraction: f64,
prev_pid_stats: &HashMap<String, (f64, Instant), S>, use_current_cpu_total: bool,
prev_pid_stats: &HashMap<String, (f64, Instant), S>,
new_pid_stats: &mut HashMap<String, (f64, Instant), S>, use_current_cpu_total: bool,
curr_time: Instant,
) -> std::io::Result<(f64, (String, (f64, Instant)))> {
) -> std::io::Result<f64> {
// Based heavily on https://stackoverflow.com/a/23376195 and https://stackoverflow.com/a/1424556
let before_proc_val: f64 = if prev_pid_stats.contains_key(&pid.to_string()) {
prev_pid_stats
@ -139,36 +140,28 @@ fn linux_cpu_usage<S: core::hash::BuildHasher>(
(after_proc_val - before_proc_val) / cpu_usage * 100_f64
);*/
let new_dict_entry = (pid.to_string(), (after_proc_val, curr_time));
new_pid_stats.insert(pid.to_string(), (after_proc_val, curr_time));
if use_current_cpu_total {
Ok((
(after_proc_val - before_proc_val) / cpu_usage * 100_f64,
new_dict_entry,
))
Ok((after_proc_val - before_proc_val) / cpu_usage * 100_f64)
} else {
Ok((
(after_proc_val - before_proc_val) / cpu_usage * 100_f64 * cpu_fraction,
new_dict_entry,
))
Ok((after_proc_val - before_proc_val) / cpu_usage * 100_f64 * cpu_fraction)
}
}
fn convert_ps<S: core::hash::BuildHasher>(
process: &str, cpu_usage: f64, cpu_fraction: f64,
prev_pid_stats: &HashMap<String, (f64, Instant), S>, use_current_cpu_total: bool,
prev_pid_stats: &HashMap<String, (f64, Instant), S>,
new_pid_stats: &mut HashMap<String, (f64, Instant), S>, use_current_cpu_total: bool,
curr_time: Instant,
) -> std::io::Result<(ProcessHarvest, (String, (f64, Instant)))> {
) -> std::io::Result<ProcessHarvest> {
if process.trim().to_string().is_empty() {
let dummy_result = (String::default(), (0.0, Instant::now()));
return Ok((
ProcessHarvest {
return Ok(ProcessHarvest {
pid: 0,
name: "".to_string(),
mem_usage_percent: 0.0,
cpu_usage_percent: 0.0,
},
dummy_result,
));
});
}
let pid = (&process[..11])
@ -183,23 +176,21 @@ fn convert_ps<S: core::hash::BuildHasher>(
.parse::<f64>()
.unwrap_or(0_f64);
let (cpu_usage_percent, new_entry) = linux_cpu_usage(
let cpu_usage_percent = linux_cpu_usage(
pid,
cpu_usage,
cpu_fraction,
prev_pid_stats,
new_pid_stats,
use_current_cpu_total,
curr_time,
)?;
Ok((
ProcessHarvest {
Ok(ProcessHarvest {
pid,
name,
mem_usage_percent,
cpu_usage_percent: cpu_usage_percent,
},
new_entry,
))
cpu_usage_percent,
})
}
pub fn get_sorted_processes_list(
@ -222,19 +213,18 @@ pub fn get_sorted_processes_list(
let mut new_pid_stats: HashMap<String, (f64, Instant), RandomState> = HashMap::new();
for process in process_stream {
if let Ok((process_object, new_entry)) = convert_ps(
if let Ok(process_object) = convert_ps(
process,
cpu_usage,
cpu_fraction,
&prev_pid_stats,
&mut new_pid_stats,
use_current_cpu_total,
curr_time,
) {
if !process_object.name.is_empty() {
process_vector.push(process_object);
}
new_pid_stats.insert(new_entry.0, new_entry.1);
}
}

View File

@ -470,10 +470,8 @@ impl Painter {
};
// Set up blocks and their components
// CPU graph
// CPU graph + legend
self.draw_cpu_graph(&mut f, &app_state, cpu_chunk[graph_index]);
// CPU legend
self.draw_cpu_legend(&mut f, app_state, cpu_chunk[legend_index]);
//Memory usage graph
@ -481,7 +479,6 @@ impl Painter {
// Network graph
self.draw_network_graph(&mut f, &app_state, network_chunk[0]);
self.draw_network_labels(&mut f, app_state, network_chunk[1]);
// Temperature table
@ -535,33 +532,24 @@ impl Painter {
.bounds([-0.5, 100.5])
.labels(&["0%", "100%"]);
let mut dataset_vector: Vec<Dataset> = Vec::new();
let mut cpu_entries_vec: Vec<(Style, Vec<(f64, f64)>)> = Vec::new();
for (itx, cpu) in cpu_data.iter().enumerate().rev() {
if app_state.cpu_state.core_show_vec[itx] {
cpu_entries_vec.push((
self.colours.cpu_colour_styles[(itx) % self.colours.cpu_colour_styles.len()],
cpu.cpu_data
let dataset_vector: Vec<Dataset> = cpu_data
.iter()
.map(<(f64, f64)>::from)
.collect::<Vec<_>>(),
));
}
}
for cpu_entry in &cpu_entries_vec {
dataset_vector.push(
.enumerate()
.rev()
.filter(|(itx, _)| app_state.cpu_state.core_show_vec[*itx])
.map(|(itx, cpu)| {
Dataset::default()
.marker(if app_state.app_config_fields.use_dot {
Marker::Dot
} else {
Marker::Braille
})
.style(cpu_entry.0)
.data(&(cpu_entry.1)),
);
}
.style(
self.colours.cpu_colour_styles[itx % self.colours.cpu_colour_styles.len()],
)
.data(&cpu.cpu_data[..])
})
.collect::<Vec<_>>();
let title = if app_state.is_expanded && !app_state.cpu_state.is_showing_tray {
const TITLE_BASE: &str = " CPU ── Esc to go back ";
@ -620,28 +608,31 @@ impl Painter {
let sliced_cpu_data = &cpu_data[start_position as usize..];
let mut stringified_cpu_data: Vec<Vec<String>> = Vec::new();
if app_state.cpu_state.is_showing_tray {
for (itx, cpu) in sliced_cpu_data.iter().enumerate() {
if let Some(cpu_data) = cpu.cpu_data.last() {
let entry = if app_state.cpu_state.is_showing_tray {
vec![
let entry = vec![
if app_state.cpu_state.core_show_vec[itx + start_position as usize] {
"[*]".to_string()
} else {
"[ ]".to_string()
},
cpu.cpu_name.clone(),
format!("{:.0}%", cpu_data.usage.round()),
]
} else {
vec![
cpu.cpu_name.clone(),
format!("{:.0}%", cpu_data.usage.round()),
]
};
format!("{:.0}%", cpu_data.1.round()),
];
stringified_cpu_data.push(entry);
}
}
} else {
for cpu in sliced_cpu_data.iter() {
if let Some(cpu_data) = cpu.cpu_data.last() {
let entry = vec![cpu.cpu_name.clone(), format!("{:.0}%", cpu_data.1.round())];
stringified_cpu_data.push(entry);
}
}
}
let cpu_rows = stringified_cpu_data
.iter()
@ -873,10 +864,7 @@ impl Painter {
.y_axis(y_axis)
.datasets(&[
Dataset::default()
.name(&format!(
"RX: {:7}",
app_state.canvas_data.rx_display.clone()
))
.name(&format!("RX: {:7}", app_state.canvas_data.rx_display))
.marker(if app_state.app_config_fields.use_dot {
Marker::Dot
} else {
@ -885,10 +873,7 @@ impl Painter {
.style(self.colours.rx_style)
.data(&network_data_rx),
Dataset::default()
.name(&format!(
"TX: {:7}",
app_state.canvas_data.tx_display.clone()
))
.name(&format!("TX: {:7}", app_state.canvas_data.tx_display))
.marker(if app_state.app_config_fields.use_dot {
Marker::Dot
} else {
@ -898,11 +883,11 @@ impl Painter {
.data(&network_data_tx),
Dataset::default().name(&format!(
"Total RX: {:7}",
app_state.canvas_data.total_rx_display.clone()
app_state.canvas_data.total_rx_display
)),
Dataset::default().name(&format!(
"Total TX: {:7}",
app_state.canvas_data.total_tx_display.clone()
app_state.canvas_data.total_tx_display
)),
])
.render(f, draw_loc);
@ -911,10 +896,10 @@ impl Painter {
fn draw_network_labels<B: backend::Backend>(
&self, f: &mut Frame<B>, app_state: &mut app::App, draw_loc: Rect,
) {
let rx_display: String = app_state.canvas_data.rx_display.clone();
let tx_display: String = app_state.canvas_data.tx_display.clone();
let total_rx_display: String = app_state.canvas_data.total_rx_display.clone();
let total_tx_display: String = app_state.canvas_data.total_tx_display.clone();
let rx_display = &app_state.canvas_data.rx_display;
let tx_display = &app_state.canvas_data.tx_display;
let total_rx_display = &app_state.canvas_data.total_rx_display;
let total_tx_display = &app_state.canvas_data.total_tx_display;
// Gross but I need it to work...
let total_network = vec![vec![
@ -1252,7 +1237,11 @@ impl Painter {
);
let title = format!("{} Esc to close ", "".repeat(repeat_num as usize));
let current_border_style: Style = if app_state.get_current_regex_matcher().is_err() {
let current_border_style: Style = if app_state
.process_search_state
.search_state
.is_invalid_search
{
Style::default().fg(Color::Rgb(255, 0, 0))
} else {
match app_state.current_widget_selected {

View File

@ -35,27 +35,7 @@ pub struct ConvertedProcessData {
#[derive(Clone, Default, Debug)]
pub struct ConvertedCpuData {
pub cpu_name: String,
pub cpu_data: Vec<CpuPoint>,
}
#[derive(Clone, Default, Debug)]
pub struct CpuPoint {
pub time: f64,
pub usage: f64,
}
impl From<CpuPoint> for (f64, f64) {
fn from(c: CpuPoint) -> (f64, f64) {
let CpuPoint { time, usage } = c;
(time, usage)
}
}
impl From<&CpuPoint> for (f64, f64) {
fn from(c: &CpuPoint) -> (f64, f64) {
let CpuPoint { time, usage } = c;
(*time, *usage)
}
pub cpu_data: Vec<(f64, f64)>,
}
pub fn convert_temp_row(app: &App) -> Vec<Vec<String>> {
@ -150,16 +130,14 @@ pub fn convert_cpu_data_points(
//Insert joiner points
for &(joiner_offset, joiner_val) in &cpu.1 {
let offset_time = time_from_start - joiner_offset as f64;
cpu_data_vector[itx_offset].cpu_data.push(CpuPoint {
time: offset_time,
usage: joiner_val,
});
cpu_data_vector[itx_offset]
.cpu_data
.push((offset_time, joiner_val));
}
cpu_data_vector[itx_offset].cpu_data.push(CpuPoint {
time: time_from_start,
usage: cpu.0,
});
cpu_data_vector[itx_offset]
.cpu_data
.push((time_from_start, cpu.0));
}
}

View File

@ -21,6 +21,7 @@ use crossterm::{
};
use std::{
boxed::Box,
io::{stdout, Write},
panic::{self, PanicInfo},
sync::mpsc,
@ -47,7 +48,7 @@ use utils::error::{self, BottomError};
enum Event<I, J> {
KeyInput(I),
MouseInput(J),
Update(data_harvester::Data),
Update(Box<data_harvester::Data>),
Clean,
}
@ -210,6 +211,7 @@ fn main() -> error::Result<()> {
painter.colours.generate_remaining_cpu_colours();
painter.initialize();
let mut first_run = true;
loop {
// TODO: [OPT] this should not block...
if let Ok(recv) = rx.recv_timeout(Duration::from_millis(TICK_RATE_IN_MILLISECONDS)) {
@ -259,11 +261,14 @@ fn main() -> error::Result<()> {
);
// Pre-fill CPU if needed
if first_run {
for itx in 0..app.canvas_data.cpu_data.len() {
if app.cpu_state.core_show_vec.len() <= itx {
app.cpu_state.core_show_vec.push(true);
}
}
first_run = false;
}
// Processes
let (single, grouped) = convert_process_data(&app.data_collection);
@ -756,22 +761,24 @@ fn update_final_process_list(app: &mut app::App) {
let mut filtered_process_data: Vec<ConvertedProcessData> = if app.is_grouped() {
app.canvas_data
.grouped_process_data
.clone()
.into_iter()
.iter()
.filter(|process| {
if app
.process_search_state
.search_state
.is_invalid_or_blank_search
.is_invalid_or_blank_search()
{
true
} else if let Ok(matcher) = app.get_current_regex_matcher() {
matcher.is_match(&process.name)
} else {
true
return true;
} else if let Some(matcher_result) = app.get_current_regex_matcher() {
if let Ok(matcher) = matcher_result {
return matcher.is_match(&process.name);
}
}
true
})
.collect::<Vec<ConvertedProcessData>>()
.cloned()
.collect::<Vec<_>>()
} else {
app.canvas_data
.process_data
@ -780,18 +787,19 @@ fn update_final_process_list(app: &mut app::App) {
if app
.process_search_state
.search_state
.is_invalid_or_blank_search
.is_invalid_or_blank_search()
{
true
} else if let Ok(matcher) = app.get_current_regex_matcher() {
return true;
} else if let Some(matcher_result) = app.get_current_regex_matcher() {
if let Ok(matcher) = matcher_result {
if app.process_search_state.is_searching_with_pid {
matcher.is_match(&process.pid.to_string())
return matcher.is_match(&process.pid.to_string());
} else {
matcher.is_match(&process.name)
return matcher.is_match(&process.name);
}
}
}
} else {
true
}
})
.map(|(_pid, process)| ConvertedProcessData {
pid: process.pid,
@ -800,7 +808,7 @@ fn update_final_process_list(app: &mut app::App) {
mem_usage: process.mem_usage_percent,
group_pids: vec![process.pid],
})
.collect::<Vec<ConvertedProcessData>>()
.collect::<Vec<_>>()
};
sort_process_data(&mut filtered_process_data, app);
@ -887,7 +895,7 @@ fn create_event_thread(
}
}
futures::executor::block_on(data_state.update_data());
let event = Event::Update(data_state.data);
let event = Event::Update(Box::from(data_state.data));
data_state.data = data_harvester::Data::default();
tx.send(event).unwrap();
thread::sleep(Duration::from_millis(update_rate_in_milliseconds));