summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClementTsang <cjhtsang@uwaterloo.ca>2020-02-17 17:42:51 -0500
committerClementTsang <cjhtsang@uwaterloo.ca>2020-02-17 17:42:51 -0500
commit4485d1b380e45b4c86cbb41ba1d5f1f3f3799721 (patch)
treef9b57d13768d2fe010bdf5b56563b79d7464ebea
parentc669b5337c5267120448b28b0cd9a48975e59dc9 (diff)
Some clippy and refactoring.
-rw-r--r--Cargo.toml5
-rw-r--r--src/app.rs93
-rw-r--r--src/app/data_harvester/processes.rs62
-rw-r--r--src/canvas.rs95
-rw-r--r--src/data_conversion.rs36
-rw-r--r--src/main.rs56
6 files changed, 157 insertions, 190 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 30dc0aa6..8490cb56 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/src/app.rs b/src/app.rs
index 12c17bd6..a5400aea 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -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 {
+ "^{}$"
+ } else {
+ ""
+ },
+ if self.process_search_state.is_ignoring_case {
+ "(?i){}"
+ } else {
+ ""
+ },
+ if !self.process_search_state.is_searching_with_regex {
+ escaped_regex = regex::escape(regex_string);
+ &escaped_regex
+ } else {
+ regex_string
+ }
+ );
- if self.process_search_state.is_searching_whole_word {
- final_regex_string = format!("^{}$", final_regex_string);
- }
- if self.process_search_state.is_ignoring_case {
- final_regex_string = format!("(?i){}", final_regex_string);
- }
+ self.process_search_state.search_state.is_blank_search = false;
- 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();
+ 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
}
diff --git a/src/app/data_harvester/processes.rs b/src/app/data_harvester/processes.rs
index cca65075..9a2bc548 100644
--- a/src/app/data_harvester/processes.rs
+++ b/src/app/data_harvester/processes.rs
@@ -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 {
- pid: 0,
- name: "".to_string(),
- mem_usage_percent: 0.0,
- cpu_usage_percent: 0.0,
- },
- dummy_result,
- ));
+ return Ok(ProcessHarvest {
+ pid: 0,
+ name: "".to_string(),
+ mem_usage_percent: 0.0,
+ cpu_usage_percent: 0.0,
+ });
}
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 {
- pid,
- name,
- mem_usage_percent,
- cpu_usage_percent: cpu_usage_percent,
- },
- new_entry,
- ))
+ Ok(ProcessHarvest {
+ pid,
+ name,
+ mem_usage_percent,
+ 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);
}
}
diff --git a/src/canvas.rs b/src/canvas.rs
index f888934c..30fc0973 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -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
- .iter()
- .map(<(f64, f64)>::from)
- .collect::<Vec<_>>(),
- ));
- }
- }
-
- for cpu_entry in &cpu_entries_vec {
- dataset_vector.push(
+ let dataset_vector: Vec<Dataset> = cpu_data
+ .iter()
+ .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,26 +608,29 @@ impl Painter {
let sliced_cpu_data = &cpu_data[start_position as usize..];
let mut stringified_cpu_data: Vec<Vec<String>> = Vec::new();
- 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![
+ 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 = 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);
+ 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);
+ }
}
}
@@ -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 {
diff --git a/src/data_conversion.rs b/src/data_conversion.rs
index 71f921b1..26cbd9c1 100644
--- a/src/data_conversion.rs
+++ b/src/data_conversion.rs
@@ -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));
}
}
diff --git a/src/main.rs b/src/main.rs
index a21d7a83..aefa5626 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -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,10 +261,13 @@ fn main() -> error::Result<()> {
);
// Pre-fill CPU if needed
- 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);
+ 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
@@ -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() {
- if app.process_search_state.is_searching_with_pid {
- matcher.is_match(&process.pid.to_string())
- } else {
- matcher.is_match(&process.name)
+ 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 {
+ return matcher.is_match(&process.pid.to_string());
+ } else {
+ return matcher.is_match(&process.name);
+ }
}
- } else {
- true
}
+ 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));