mirror of
https://github.com/ClementTsang/bottom
synced 2024-12-18 00:23:15 +00:00
Merge branch 'master' into populate-config
This commit is contained in:
commit
735038f060
19 changed files with 233 additions and 131 deletions
4
.github/pull_request_template.md
vendored
4
.github/pull_request_template.md
vendored
|
@ -14,6 +14,7 @@ _Remove the irrelevant one:_
|
||||||
|
|
||||||
- [x] Bug fix (non-breaking change which fixes an issue)
|
- [x] Bug fix (non-breaking change which fixes an issue)
|
||||||
- [x] New feature (non-breaking change which adds functionality)
|
- [x] New feature (non-breaking change which adds functionality)
|
||||||
|
- [x] Other (something else)
|
||||||
|
|
||||||
## Test methodology
|
## Test methodology
|
||||||
|
|
||||||
|
@ -24,10 +25,11 @@ _Please state how this was tested:_
|
||||||
_Please ensure all are ticked (and actually done):_
|
_Please ensure all are ticked (and actually done):_
|
||||||
|
|
||||||
- [ ] Change has been tested to work
|
- [ ] Change has been tested to work
|
||||||
- [ ] Code has been linted using rustfmt
|
- [ ] Areas your change affects has been linted using rustfmt
|
||||||
- [ ] Code has been self-reviewed
|
- [ ] Code has been self-reviewed
|
||||||
- [ ] Code has been tested and no new breakage is introduced
|
- [ ] Code has been tested and no new breakage is introduced
|
||||||
- [ ] Documentation has been added/updated if needed
|
- [ ] Documentation has been added/updated if needed
|
||||||
|
- [ ] No merge conflicts arise from the change
|
||||||
|
|
||||||
## Other information
|
## Other information
|
||||||
|
|
||||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -15,5 +15,6 @@ Cargo.lock
|
||||||
rust-unmangle
|
rust-unmangle
|
||||||
*.svg
|
*.svg
|
||||||
*.data
|
*.data
|
||||||
|
.idea/
|
||||||
|
|
||||||
sample_configs/testing.toml
|
sample_configs/testing.toml
|
||||||
|
|
|
@ -30,7 +30,6 @@ heim = "0.0.10"
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
regex = "1.3.4"
|
regex = "1.3.4"
|
||||||
sysinfo = "0.11"
|
sysinfo = "0.11"
|
||||||
winapi = "0.3.8"
|
|
||||||
crossterm = "0.16"
|
crossterm = "0.16"
|
||||||
tui = {version = "0.8", features = ["crossterm"], default-features = false }
|
tui = {version = "0.8", features = ["crossterm"], default-features = false }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
|
@ -40,6 +39,9 @@ serde = {version = "1.0", features = ["derive"] }
|
||||||
unicode-segmentation = "1.6.0"
|
unicode-segmentation = "1.6.0"
|
||||||
unicode-width = "0.1.7"
|
unicode-width = "0.1.7"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = "0.3.8"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
assert_cmd = "0.12"
|
assert_cmd = "0.12"
|
||||||
predicates = "1"
|
predicates = "1"
|
||||||
|
|
|
@ -206,7 +206,7 @@ Note that `q` is disabled while in the search widget.
|
||||||
|
|
||||||
## Contribution
|
## Contribution
|
||||||
|
|
||||||
Contribution is welcome! Just submit a PR.
|
Contribution is welcome! Just submit a PR. Note that I develop and test on stable Rust.
|
||||||
|
|
||||||
If you spot any issue with nobody assigned to it, or it seems like no work has started on it, feel free to try and do it!
|
If you spot any issue with nobody assigned to it, or it seems like no work has started on it, feel free to try and do it!
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,7 @@ max_width = 100
|
||||||
newline_style = "Unix"
|
newline_style = "Unix"
|
||||||
reorder_imports = true
|
reorder_imports = true
|
||||||
fn_args_layout = "Compressed"
|
fn_args_layout = "Compressed"
|
||||||
empty_item_single_line = false
|
|
||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
merge_imports = true
|
|
||||||
merge_derives = true
|
merge_derives = true
|
||||||
reorder_modules = true
|
reorder_modules = true
|
||||||
reorder_impl_items = true
|
|
||||||
tab_spaces = 4
|
tab_spaces = 4
|
||||||
format_strings = true
|
|
||||||
space_after_colon = true
|
|
||||||
|
|
82
src/app.rs
82
src/app.rs
|
@ -1,15 +1,16 @@
|
||||||
pub mod data_harvester;
|
|
||||||
use data_harvester::{processes, temperature};
|
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
pub mod data_farmer;
|
use unicode_segmentation::GraphemeCursor;
|
||||||
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||||
|
|
||||||
use data_farmer::*;
|
use data_farmer::*;
|
||||||
|
use data_harvester::{processes, temperature};
|
||||||
|
|
||||||
use crate::{canvas, constants, utils::error::Result};
|
use crate::{canvas, constants, utils::error::Result};
|
||||||
mod process_killer;
|
|
||||||
|
|
||||||
use unicode_segmentation::{GraphemeCursor};
|
pub mod data_farmer;
|
||||||
use unicode_width::UnicodeWidthStr;
|
pub mod data_harvester;
|
||||||
|
mod process_killer;
|
||||||
|
|
||||||
const MAX_SEARCH_LENGTH: usize = 200;
|
const MAX_SEARCH_LENGTH: usize = 200;
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ pub enum ScrollDirection {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SearchDirection {
|
pub enum CursorDirection {
|
||||||
LEFT,
|
LEFT,
|
||||||
RIGHT,
|
RIGHT,
|
||||||
}
|
}
|
||||||
|
@ -73,6 +74,10 @@ pub struct AppSearchState {
|
||||||
pub is_blank_search: bool,
|
pub is_blank_search: bool,
|
||||||
pub is_invalid_search: bool,
|
pub is_invalid_search: bool,
|
||||||
pub grapheme_cursor: GraphemeCursor,
|
pub grapheme_cursor: GraphemeCursor,
|
||||||
|
pub cursor_direction: CursorDirection,
|
||||||
|
pub cursor_bar: usize,
|
||||||
|
/// This represents the position in terms of CHARACTERS, not graphemes
|
||||||
|
pub char_cursor_position: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AppSearchState {
|
impl Default for AppSearchState {
|
||||||
|
@ -84,11 +89,21 @@ impl Default for AppSearchState {
|
||||||
is_invalid_search: false,
|
is_invalid_search: false,
|
||||||
is_blank_search: true,
|
is_blank_search: true,
|
||||||
grapheme_cursor: GraphemeCursor::new(0, 0, true),
|
grapheme_cursor: GraphemeCursor::new(0, 0, true),
|
||||||
|
cursor_direction: CursorDirection::RIGHT,
|
||||||
|
cursor_bar: 0,
|
||||||
|
char_cursor_position: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AppSearchState {
|
impl AppSearchState {
|
||||||
|
/// Returns a reset but still enabled app search state
|
||||||
|
pub fn reset() -> Self {
|
||||||
|
let mut app_search_state = AppSearchState::default();
|
||||||
|
app_search_state.is_enabled = true;
|
||||||
|
app_search_state
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_invalid_or_blank_search(&self) -> bool {
|
pub fn is_invalid_or_blank_search(&self) -> bool {
|
||||||
self.is_blank_search || self.is_invalid_search
|
self.is_blank_search || self.is_invalid_search
|
||||||
}
|
}
|
||||||
|
@ -549,6 +564,10 @@ impl App {
|
||||||
.cur_cursor()
|
.cur_cursor()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_char_cursor_position(&self) -> usize {
|
||||||
|
self.process_search_state.search_state.char_cursor_position
|
||||||
|
}
|
||||||
|
|
||||||
/// One of two functions allowed to run while in a dialog...
|
/// One of two functions allowed to run while in a dialog...
|
||||||
pub fn on_enter(&mut self) {
|
pub fn on_enter(&mut self) {
|
||||||
if self.delete_dialog_state.is_showing_dd {
|
if self.delete_dialog_state.is_showing_dd {
|
||||||
|
@ -620,19 +639,14 @@ impl App {
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
pub fn skip_word_backspace(&mut self) {
|
pub fn skip_word_backspace(&mut self) {
|
||||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||||
if self.process_search_state.search_state.is_enabled {
|
if self.process_search_state.search_state.is_enabled {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_search(&mut self) {
|
pub fn clear_search(&mut self) {
|
||||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||||
self.process_search_state.search_state.grapheme_cursor =
|
|
||||||
GraphemeCursor::new(0, 0, true);
|
|
||||||
self.process_search_state.search_state.current_search_query = String::default();
|
|
||||||
self.process_search_state.search_state.is_blank_search = true;
|
|
||||||
self.process_search_state.search_state.is_invalid_search = false;
|
|
||||||
self.update_process_gui = true;
|
self.update_process_gui = true;
|
||||||
|
self.process_search_state.search_state = AppSearchState::reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -663,7 +677,8 @@ impl App {
|
||||||
if self.process_search_state.search_state.is_enabled && self.get_cursor_position() > 0 {
|
if self.process_search_state.search_state.is_enabled && self.get_cursor_position() > 0 {
|
||||||
self.search_walk_back(self.get_cursor_position());
|
self.search_walk_back(self.get_cursor_position());
|
||||||
|
|
||||||
self.process_search_state
|
let removed_char = self
|
||||||
|
.process_search_state
|
||||||
.search_state
|
.search_state
|
||||||
.current_search_query
|
.current_search_query
|
||||||
.remove(self.get_cursor_position());
|
.remove(self.get_cursor_position());
|
||||||
|
@ -677,6 +692,10 @@ impl App {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
self.process_search_state.search_state.char_cursor_position -=
|
||||||
|
UnicodeWidthChar::width(removed_char).unwrap_or(0);
|
||||||
|
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
|
||||||
|
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.update_process_gui = true;
|
||||||
}
|
}
|
||||||
|
@ -710,7 +729,15 @@ impl App {
|
||||||
pub fn on_left_key(&mut self) {
|
pub fn on_left_key(&mut self) {
|
||||||
if !self.is_in_dialog() {
|
if !self.is_in_dialog() {
|
||||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||||
|
let prev_cursor = self.get_cursor_position();
|
||||||
self.search_walk_back(self.get_cursor_position());
|
self.search_walk_back(self.get_cursor_position());
|
||||||
|
if self.get_cursor_position() < prev_cursor {
|
||||||
|
let str_slice = &self.process_search_state.search_state.current_search_query
|
||||||
|
[self.get_cursor_position()..prev_cursor];
|
||||||
|
self.process_search_state.search_state.char_cursor_position -=
|
||||||
|
UnicodeWidthStr::width(str_slice);
|
||||||
|
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes {
|
} else if self.delete_dialog_state.is_showing_dd && !self.delete_dialog_state.is_on_yes {
|
||||||
self.delete_dialog_state.is_on_yes = true;
|
self.delete_dialog_state.is_on_yes = true;
|
||||||
|
@ -720,7 +747,16 @@ impl App {
|
||||||
pub fn on_right_key(&mut self) {
|
pub fn on_right_key(&mut self) {
|
||||||
if !self.is_in_dialog() {
|
if !self.is_in_dialog() {
|
||||||
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
if let WidgetPosition::ProcessSearch = self.current_widget_selected {
|
||||||
|
let prev_cursor = self.get_cursor_position();
|
||||||
self.search_walk_forward(self.get_cursor_position());
|
self.search_walk_forward(self.get_cursor_position());
|
||||||
|
if self.get_cursor_position() > prev_cursor {
|
||||||
|
let str_slice = &self.process_search_state.search_state.current_search_query
|
||||||
|
[prev_cursor..self.get_cursor_position()];
|
||||||
|
self.process_search_state.search_state.char_cursor_position +=
|
||||||
|
UnicodeWidthStr::width(str_slice);
|
||||||
|
self.process_search_state.search_state.cursor_direction =
|
||||||
|
CursorDirection::RIGHT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes {
|
} else if self.delete_dialog_state.is_showing_dd && self.delete_dialog_state.is_on_yes {
|
||||||
self.delete_dialog_state.is_on_yes = false;
|
self.delete_dialog_state.is_on_yes = false;
|
||||||
|
@ -738,6 +774,8 @@ impl App {
|
||||||
.len(),
|
.len(),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
self.process_search_state.search_state.char_cursor_position = 0;
|
||||||
|
self.process_search_state.search_state.cursor_direction = CursorDirection::LEFT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -756,6 +794,14 @@ impl App {
|
||||||
.len(),
|
.len(),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
self.process_search_state.search_state.char_cursor_position =
|
||||||
|
UnicodeWidthStr::width(
|
||||||
|
self.process_search_state
|
||||||
|
.search_state
|
||||||
|
.current_search_query
|
||||||
|
.as_str(),
|
||||||
|
);
|
||||||
|
self.process_search_state.search_state.cursor_direction = CursorDirection::RIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -840,6 +886,10 @@ impl App {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
self.search_walk_forward(self.get_cursor_position());
|
self.search_walk_forward(self.get_cursor_position());
|
||||||
|
|
||||||
|
self.process_search_state.search_state.char_cursor_position +=
|
||||||
|
UnicodeWidthChar::width(caught_char).unwrap_or(0);
|
||||||
|
|
||||||
self.update_regex();
|
self.update_regex();
|
||||||
self.update_process_gui = true;
|
self.update_process_gui = true;
|
||||||
}
|
}
|
||||||
|
@ -982,7 +1032,7 @@ impl App {
|
||||||
pub fn kill_highlighted_process(&mut self) -> Result<()> {
|
pub fn kill_highlighted_process(&mut self) -> Result<()> {
|
||||||
// Technically unnecessary but this is a good check...
|
// Technically unnecessary but this is a good check...
|
||||||
if let WidgetPosition::Process = self.current_widget_selected {
|
if let WidgetPosition::Process = self.current_widget_selected {
|
||||||
if let Some(current_selected_processes) = &(self.to_delete_process_list) {
|
if let Some(current_selected_processes) = &self.to_delete_process_list {
|
||||||
for pid in ¤t_selected_processes.1 {
|
for pid in ¤t_selected_processes.1 {
|
||||||
process_killer::kill_process_given_pid(*pid)?;
|
process_killer::kill_process_given_pid(*pid)?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Data};
|
|
||||||
/// In charge of cleaning, processing, and managing data. I couldn't think of
|
/// In charge of cleaning, processing, and managing data. I couldn't think of
|
||||||
/// a better name for the file. Since I called data collection "harvesting",
|
/// a better name for the file. Since I called data collection "harvesting",
|
||||||
/// then this is the farmer I guess.
|
/// then this is the farmer I guess.
|
||||||
|
@ -16,6 +15,8 @@ use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Da
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
|
use crate::data_harvester::{cpu, disks, mem, network, processes, temperature, Data};
|
||||||
|
|
||||||
pub type TimeOffset = f64;
|
pub type TimeOffset = f64;
|
||||||
pub type Value = f64;
|
pub type Value = f64;
|
||||||
pub type JoinedDataPoints = (Value, Vec<(TimeOffset, Value)>);
|
pub type JoinedDataPoints = (Value, Vec<(TimeOffset, Value)>);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! This is the main file to house data collection functions.
|
//! This is the main file to house data collection functions.
|
||||||
|
|
||||||
use std::{collections::HashMap, time::Instant};
|
use std::{collections::HashMap, time::Instant};
|
||||||
|
|
||||||
use sysinfo::{System, SystemExt};
|
use sysinfo::{System, SystemExt};
|
||||||
|
|
||||||
pub mod cpu;
|
pub mod cpu;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use heim::net;
|
use heim::net;
|
||||||
use heim::units::information::byte;
|
use heim::units::information::byte;
|
||||||
use std::time::Instant;
|
|
||||||
use sysinfo::{NetworkExt, System, SystemExt};
|
use sysinfo::{NetworkExt, System, SystemExt};
|
||||||
|
|
||||||
#[derive(Default, Clone, Debug)]
|
#[derive(Default, Clone, Debug)]
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::utils::error;
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::{hash_map::RandomState, HashMap},
|
collections::{hash_map::RandomState, HashMap},
|
||||||
process::Command,
|
process::Command,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
|
use sysinfo::{ProcessExt, ProcessorExt, System, SystemExt};
|
||||||
|
|
||||||
|
use crate::utils::error;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ProcessSorting {
|
pub enum ProcessSorting {
|
||||||
CPU,
|
CPU,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use heim::units::thermodynamic_temperature;
|
use heim::units::thermodynamic_temperature;
|
||||||
use std::cmp::Ordering;
|
|
||||||
use sysinfo::{ComponentExt, System, SystemExt};
|
use sysinfo::{ComponentExt, System, SystemExt};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone)]
|
#[derive(Default, Debug, Clone)]
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/// This file is meant to house (OS specific) implementations on how to kill processes.
|
|
||||||
use crate::utils::error::BottomError;
|
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
// Copied from SO: https://stackoverflow.com/a/55231715
|
// Copied from SO: https://stackoverflow.com/a/55231715
|
||||||
|
@ -12,6 +10,9 @@ use winapi::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// This file is meant to house (OS specific) implementations on how to kill processes.
|
||||||
|
use crate::utils::error::BottomError;
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
struct Process(HANDLE);
|
struct Process(HANDLE);
|
||||||
|
|
||||||
|
|
128
src/canvas.rs
128
src/canvas.rs
|
@ -1,11 +1,6 @@
|
||||||
use crate::{
|
use std::cmp::max;
|
||||||
app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition},
|
|
||||||
constants::*,
|
|
||||||
data_conversion::{ConvertedCpuData, ConvertedProcessData},
|
|
||||||
utils::error,
|
|
||||||
};
|
|
||||||
use std::cmp::{max, min};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use tui::{
|
use tui::{
|
||||||
backend,
|
backend,
|
||||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||||
|
@ -17,12 +12,19 @@ use tui::{
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
mod canvas_colours;
|
|
||||||
use canvas_colours::*;
|
use canvas_colours::*;
|
||||||
|
|
||||||
mod drawing_utils;
|
|
||||||
use drawing_utils::*;
|
use drawing_utils::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
app::{self, data_harvester::processes::ProcessHarvest, WidgetPosition},
|
||||||
|
constants::*,
|
||||||
|
data_conversion::{ConvertedCpuData, ConvertedProcessData},
|
||||||
|
utils::error,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod canvas_colours;
|
||||||
|
mod drawing_utils;
|
||||||
|
|
||||||
// Headers
|
// Headers
|
||||||
const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
|
const CPU_LEGEND_HEADER: [&str; 2] = ["CPU", "Use%"];
|
||||||
const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show (Space)"];
|
const CPU_SELECT_LEGEND_HEADER: [&str; 2] = ["CPU", "Show (Space)"];
|
||||||
|
@ -620,12 +622,12 @@ impl Painter {
|
||||||
fn draw_cpu_legend<B: backend::Backend>(
|
fn draw_cpu_legend<B: backend::Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let cpu_data: &[ConvertedCpuData] = &(app_state.canvas_data.cpu_data);
|
let cpu_data: &[ConvertedCpuData] = &app_state.canvas_data.cpu_data;
|
||||||
|
|
||||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||||
let start_position = get_start_position(
|
let start_position = get_start_position(
|
||||||
num_rows,
|
num_rows,
|
||||||
&(app_state.app_scroll_positions.scroll_direction),
|
&app_state.app_scroll_positions.scroll_direction,
|
||||||
&mut app_state
|
&mut app_state
|
||||||
.app_scroll_positions
|
.app_scroll_positions
|
||||||
.cpu_scroll_state
|
.cpu_scroll_state
|
||||||
|
@ -766,8 +768,8 @@ impl Painter {
|
||||||
fn draw_memory_graph<B: backend::Backend>(
|
fn draw_memory_graph<B: backend::Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let mem_data: &[(f64, f64)] = &(app_state.canvas_data.mem_data);
|
let mem_data: &[(f64, f64)] = &app_state.canvas_data.mem_data;
|
||||||
let swap_data: &[(f64, f64)] = &(app_state.canvas_data.swap_data);
|
let swap_data: &[(f64, f64)] = &app_state.canvas_data.swap_data;
|
||||||
|
|
||||||
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]);
|
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, TIME_STARTS_FROM as f64]);
|
||||||
|
|
||||||
|
@ -839,8 +841,8 @@ impl Painter {
|
||||||
fn draw_network_graph<B: backend::Backend>(
|
fn draw_network_graph<B: backend::Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &app::App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let network_data_rx: &[(f64, f64)] = &(app_state.canvas_data.network_data_rx);
|
let network_data_rx: &[(f64, f64)] = &app_state.canvas_data.network_data_rx;
|
||||||
let network_data_tx: &[(f64, f64)] = &(app_state.canvas_data.network_data_tx);
|
let network_data_tx: &[(f64, f64)] = &app_state.canvas_data.network_data_tx;
|
||||||
|
|
||||||
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]);
|
let x_axis: Axis<'_, String> = Axis::default().bounds([0.0, 60_000.0]);
|
||||||
let y_axis: Axis<'_, &str> = Axis::default()
|
let y_axis: Axis<'_, &str> = Axis::default()
|
||||||
|
@ -938,7 +940,7 @@ impl Painter {
|
||||||
|
|
||||||
// Calculate widths
|
// Calculate widths
|
||||||
let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25];
|
let width_ratios: Vec<f64> = vec![0.25, 0.25, 0.25, 0.25];
|
||||||
let lens: &Vec<usize> = &NETWORK_HEADERS_LENS;
|
let lens: &[usize] = &NETWORK_HEADERS_LENS;
|
||||||
let width = f64::from(draw_loc.width);
|
let width = f64::from(draw_loc.width);
|
||||||
|
|
||||||
let variable_intrinsic_results =
|
let variable_intrinsic_results =
|
||||||
|
@ -967,12 +969,12 @@ impl Painter {
|
||||||
fn draw_temp_table<B: backend::Backend>(
|
fn draw_temp_table<B: backend::Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let temp_sensor_data: &[Vec<String>] = &(app_state.canvas_data.temp_sensor_data);
|
let temp_sensor_data: &[Vec<String>] = &app_state.canvas_data.temp_sensor_data;
|
||||||
|
|
||||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||||
let start_position = get_start_position(
|
let start_position = get_start_position(
|
||||||
num_rows,
|
num_rows,
|
||||||
&(app_state.app_scroll_positions.scroll_direction),
|
&app_state.app_scroll_positions.scroll_direction,
|
||||||
&mut app_state
|
&mut app_state
|
||||||
.app_scroll_positions
|
.app_scroll_positions
|
||||||
.temp_scroll_state
|
.temp_scroll_state
|
||||||
|
@ -984,7 +986,7 @@ impl Painter {
|
||||||
app_state.is_resized,
|
app_state.is_resized,
|
||||||
);
|
);
|
||||||
|
|
||||||
let sliced_vec = &(temp_sensor_data[start_position as usize..]);
|
let sliced_vec = &temp_sensor_data[start_position as usize..];
|
||||||
let mut temp_row_counter: i64 = 0;
|
let mut temp_row_counter: i64 = 0;
|
||||||
|
|
||||||
let temperature_rows = sliced_vec.iter().map(|temp_row| {
|
let temperature_rows = sliced_vec.iter().map(|temp_row| {
|
||||||
|
@ -1064,11 +1066,11 @@ impl Painter {
|
||||||
fn draw_disk_table<B: backend::Backend>(
|
fn draw_disk_table<B: backend::Backend>(
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let disk_data: &[Vec<String>] = &(app_state.canvas_data.disk_data);
|
let disk_data: &[Vec<String>] = &app_state.canvas_data.disk_data;
|
||||||
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
let num_rows = max(0, i64::from(draw_loc.height) - 5) as u64;
|
||||||
let start_position = get_start_position(
|
let start_position = get_start_position(
|
||||||
num_rows,
|
num_rows,
|
||||||
&(app_state.app_scroll_positions.scroll_direction),
|
&app_state.app_scroll_positions.scroll_direction,
|
||||||
&mut app_state
|
&mut app_state
|
||||||
.app_scroll_positions
|
.app_scroll_positions
|
||||||
.disk_scroll_state
|
.disk_scroll_state
|
||||||
|
@ -1162,27 +1164,27 @@ impl Painter {
|
||||||
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
&self, f: &mut Frame<'_, B>, app_state: &mut app::App, draw_loc: Rect,
|
||||||
) {
|
) {
|
||||||
let width = max(0, draw_loc.width as i64 - 34) as u64; // TODO: [REFACTOR] Hard coding this is terrible.
|
let width = max(0, draw_loc.width as i64 - 34) as u64; // TODO: [REFACTOR] Hard coding this is terrible.
|
||||||
let query = app_state.get_current_search_query().as_str();
|
|
||||||
let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true).rev(); // Reverse due to us wanting to draw from back -> front
|
|
||||||
let cursor_position = app_state.get_cursor_position();
|
let cursor_position = app_state.get_cursor_position();
|
||||||
let right_border = min(UnicodeWidthStr::width(query), width as usize);
|
let char_cursor_position = app_state.get_char_cursor_position();
|
||||||
|
|
||||||
let mut itx = 0;
|
let start_position: usize = get_search_start_position(
|
||||||
let mut query_with_cursor: Vec<Text<'_>> = if let app::WidgetPosition::ProcessSearch =
|
width as usize,
|
||||||
app_state.current_widget_selected
|
&app_state.process_search_state.search_state.cursor_direction,
|
||||||
{
|
&mut app_state.process_search_state.search_state.cursor_bar,
|
||||||
let mut res = Vec::new();
|
char_cursor_position,
|
||||||
if cursor_position >= query.len() {
|
app_state.is_resized,
|
||||||
res.push(Text::styled(
|
);
|
||||||
" ",
|
|
||||||
self.colours.currently_selected_text_style,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
res.extend(
|
let query = app_state.get_current_search_query().as_str();
|
||||||
grapheme_indices
|
let grapheme_indices = UnicodeSegmentation::grapheme_indices(query, true);
|
||||||
|
let mut current_grapheme_posn = 0;
|
||||||
|
let query_with_cursor: Vec<Text<'_>> =
|
||||||
|
if let app::WidgetPosition::ProcessSearch = app_state.current_widget_selected {
|
||||||
|
let mut res = grapheme_indices
|
||||||
.filter_map(|grapheme| {
|
.filter_map(|grapheme| {
|
||||||
if itx >= right_border {
|
current_grapheme_posn += UnicodeWidthStr::width(grapheme.1);
|
||||||
|
|
||||||
|
if current_grapheme_posn <= start_position {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
let styled = if grapheme.0 == cursor_position {
|
let styled = if grapheme.0 == cursor_position {
|
||||||
|
@ -1190,32 +1192,34 @@ impl Painter {
|
||||||
} else {
|
} else {
|
||||||
Text::styled(grapheme.1, self.colours.text_style)
|
Text::styled(grapheme.1, self.colours.text_style)
|
||||||
};
|
};
|
||||||
itx += UnicodeWidthStr::width(grapheme.1);
|
|
||||||
Some(styled)
|
Some(styled)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>();
|
||||||
);
|
|
||||||
|
|
||||||
res
|
if cursor_position >= query.len() {
|
||||||
} else {
|
res.push(Text::styled(
|
||||||
// This is easier - we just need to get a range of graphemes, rather than
|
" ",
|
||||||
// dealing with possibly inserting a cursor (as none is shown!)
|
self.colours.currently_selected_text_style,
|
||||||
grapheme_indices
|
))
|
||||||
.filter_map(|grapheme| {
|
}
|
||||||
if itx >= right_border {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
let styled = Text::styled(grapheme.1, self.colours.text_style);
|
|
||||||
itx += UnicodeWidthStr::width(grapheme.1);
|
|
||||||
Some(styled)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
};
|
|
||||||
|
|
||||||
// I feel like this is most definitely not the efficient way of doing this but eh
|
res
|
||||||
query_with_cursor.reverse();
|
} else {
|
||||||
|
// This is easier - we just need to get a range of graphemes, rather than
|
||||||
|
// dealing with possibly inserting a cursor (as none is shown!)
|
||||||
|
grapheme_indices
|
||||||
|
.filter_map(|grapheme| {
|
||||||
|
current_grapheme_posn += UnicodeWidthStr::width(grapheme.1);
|
||||||
|
if current_grapheme_posn <= start_position {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let styled = Text::styled(grapheme.1, self.colours.text_style);
|
||||||
|
Some(styled)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
};
|
||||||
|
|
||||||
let mut search_text = vec![if app_state.is_grouped() {
|
let mut search_text = vec![if app_state.is_grouped() {
|
||||||
Text::styled("Search by Name: ", self.colours.table_header_style)
|
Text::styled("Search by Name: ", self.colours.table_header_style)
|
||||||
|
@ -1348,7 +1352,7 @@ impl Painter {
|
||||||
|
|
||||||
let position = get_start_position(
|
let position = get_start_position(
|
||||||
num_rows,
|
num_rows,
|
||||||
&(app_state.app_scroll_positions.scroll_direction),
|
&app_state.app_scroll_positions.scroll_direction,
|
||||||
&mut app_state
|
&mut app_state
|
||||||
.app_scroll_positions
|
.app_scroll_positions
|
||||||
.process_scroll_state
|
.process_scroll_state
|
||||||
|
@ -1367,7 +1371,7 @@ impl Painter {
|
||||||
position
|
position
|
||||||
};
|
};
|
||||||
|
|
||||||
let sliced_vec = &(process_data[start_position as usize..]);
|
let sliced_vec = &process_data[start_position as usize..];
|
||||||
let mut process_counter: i64 = 0;
|
let mut process_counter: i64 = 0;
|
||||||
|
|
||||||
// Draw!
|
// Draw!
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
mod colour_utils;
|
|
||||||
use colour_utils::*;
|
|
||||||
use tui::style::{Color, Modifier, Style};
|
use tui::style::{Color, Modifier, Style};
|
||||||
|
|
||||||
|
use colour_utils::*;
|
||||||
|
|
||||||
use crate::{constants::*, utils::error};
|
use crate::{constants::*, utils::error};
|
||||||
|
|
||||||
|
mod colour_utils;
|
||||||
|
|
||||||
pub struct CanvasColours {
|
pub struct CanvasColours {
|
||||||
pub currently_selected_text_colour: Color,
|
pub currently_selected_text_colour: Color,
|
||||||
pub currently_selected_bg_colour: Color,
|
pub currently_selected_bg_colour: Color,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use crate::utils::{error, gen_util::*};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use tui::style::{Color, Style};
|
use tui::style::{Color, Style};
|
||||||
|
|
||||||
|
use crate::utils::{error, gen_util::*};
|
||||||
|
|
||||||
const GOLDEN_RATIO: f32 = 0.618_034; // Approx, good enough for use (also Clippy gets mad if it's too long)
|
const GOLDEN_RATIO: f32 = 0.618_034; // Approx, good enough for use (also Clippy gets mad if it's too long)
|
||||||
pub const STANDARD_FIRST_COLOUR: Color = Color::LightMagenta;
|
pub const STANDARD_FIRST_COLOUR: Color = Color::LightMagenta;
|
||||||
pub const STANDARD_SECOND_COLOUR: Color = Color::LightYellow;
|
pub const STANDARD_SECOND_COLOUR: Color = Color::LightYellow;
|
||||||
|
|
|
@ -72,11 +72,43 @@ pub fn get_variable_intrinsic_widths(
|
||||||
|
|
||||||
#[allow(dead_code, unused_variables)]
|
#[allow(dead_code, unused_variables)]
|
||||||
pub fn get_search_start_position(
|
pub fn get_search_start_position(
|
||||||
num_rows: u64, scroll_direction: &app::ScrollDirection, scroll_position_bar: &mut u64,
|
num_columns: usize, cursor_direction: &app::CursorDirection, cursor_bar: &mut usize,
|
||||||
currently_selected_position: u64, is_resized: bool,
|
current_cursor_position: usize, is_resized: bool,
|
||||||
) -> u64 {
|
) -> usize {
|
||||||
//TODO: [Scroll] Gotta fix this too lol
|
if is_resized {
|
||||||
0
|
*cursor_bar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
match cursor_direction {
|
||||||
|
app::CursorDirection::RIGHT => {
|
||||||
|
if current_cursor_position < *cursor_bar + num_columns {
|
||||||
|
// If, using previous_scrolled_position, we can see the element
|
||||||
|
// (so within that and + num_rows) just reuse the current previously scrolled position
|
||||||
|
*cursor_bar
|
||||||
|
} else if current_cursor_position >= num_columns {
|
||||||
|
// Else if the current position past the last element visible in the list, omit
|
||||||
|
// until we can see that element
|
||||||
|
*cursor_bar = current_cursor_position - num_columns;
|
||||||
|
*cursor_bar
|
||||||
|
} else {
|
||||||
|
// Else, if it is not past the last element visible, do not omit anything
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
app::CursorDirection::LEFT => {
|
||||||
|
if current_cursor_position <= *cursor_bar {
|
||||||
|
// If it's past the first element, then show from that element downwards
|
||||||
|
*cursor_bar = current_cursor_position;
|
||||||
|
*cursor_bar
|
||||||
|
} else if current_cursor_position >= *cursor_bar + num_columns {
|
||||||
|
*cursor_bar = current_cursor_position - num_columns;
|
||||||
|
*cursor_bar
|
||||||
|
} else {
|
||||||
|
// Else, don't change what our start position is from whatever it is set to!
|
||||||
|
*cursor_bar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_start_position(
|
pub fn get_start_position(
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
//! This mainly concerns converting collected data into things that the canvas
|
//! This mainly concerns converting collected data into things that the canvas
|
||||||
//! can actually handle.
|
//! can actually handle.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use constants::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
data_farmer,
|
data_farmer,
|
||||||
|
@ -10,8 +14,6 @@ use crate::{
|
||||||
constants,
|
constants,
|
||||||
utils::gen_util::{get_exact_byte_values, get_simple_byte_values},
|
utils::gen_util::{get_exact_byte_values, get_simple_byte_values},
|
||||||
};
|
};
|
||||||
use constants::*;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct ConvertedNetworkData {
|
pub struct ConvertedNetworkData {
|
||||||
|
|
57
src/main.rs
57
src/main.rs
|
@ -1,15 +1,22 @@
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
|
extern crate futures;
|
||||||
|
#[macro_use]
|
||||||
extern crate lazy_static;
|
extern crate lazy_static;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate futures;
|
extern crate log;
|
||||||
|
|
||||||
use serde::Deserialize;
|
use std::{
|
||||||
|
boxed::Box,
|
||||||
|
io::{stdout, Write},
|
||||||
|
panic::{self, PanicInfo},
|
||||||
|
sync::mpsc,
|
||||||
|
thread,
|
||||||
|
time::{Duration, Instant},
|
||||||
|
};
|
||||||
|
|
||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::{
|
event::{
|
||||||
|
@ -20,17 +27,17 @@ use crossterm::{
|
||||||
style::Print,
|
style::Print,
|
||||||
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
|
||||||
};
|
};
|
||||||
|
use serde::Deserialize;
|
||||||
use std::{
|
|
||||||
boxed::Box,
|
|
||||||
io::{stdout, Write},
|
|
||||||
panic::{self, PanicInfo},
|
|
||||||
sync::mpsc,
|
|
||||||
thread,
|
|
||||||
time::{Duration, Instant},
|
|
||||||
};
|
|
||||||
use tui::{backend::CrosstermBackend, Terminal};
|
use tui::{backend::CrosstermBackend, Terminal};
|
||||||
|
|
||||||
|
use app::{
|
||||||
|
data_harvester::{self, processes::ProcessSorting},
|
||||||
|
App,
|
||||||
|
};
|
||||||
|
use constants::*;
|
||||||
|
use data_conversion::*;
|
||||||
|
use utils::error::{self, BottomError};
|
||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
mod utils {
|
mod utils {
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -41,14 +48,6 @@ mod canvas;
|
||||||
mod constants;
|
mod constants;
|
||||||
mod data_conversion;
|
mod data_conversion;
|
||||||
|
|
||||||
use app::{
|
|
||||||
data_harvester::{self, processes::ProcessSorting},
|
|
||||||
App,
|
|
||||||
};
|
|
||||||
use constants::*;
|
|
||||||
use data_conversion::*;
|
|
||||||
use utils::error::{self, BottomError};
|
|
||||||
|
|
||||||
enum Event<I, J> {
|
enum Event<I, J> {
|
||||||
KeyInput(I),
|
KeyInput(I),
|
||||||
MouseInput(J),
|
MouseInput(J),
|
||||||
|
@ -398,6 +397,8 @@ fn handle_key_event_or_break(
|
||||||
KeyCode::Char('u') => app.clear_search(),
|
KeyCode::Char('u') => app.clear_search(),
|
||||||
KeyCode::Char('a') => app.skip_cursor_beginning(),
|
KeyCode::Char('a') => app.skip_cursor_beginning(),
|
||||||
KeyCode::Char('e') => app.skip_cursor_end(),
|
KeyCode::Char('e') => app.skip_cursor_end(),
|
||||||
|
// Can't do now, CTRL+BACKSPACE doesn't work and graphemes
|
||||||
|
// are hard to iter while truncating last (eloquently).
|
||||||
// KeyCode::Backspace => app.skip_word_backspace(),
|
// KeyCode::Backspace => app.skip_word_backspace(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -496,22 +497,22 @@ fn get_temperature_option(
|
||||||
} else if let Some(flags) = &config.flags {
|
} else if let Some(flags) = &config.flags {
|
||||||
if let Some(temp_type) = &flags.temperature_type {
|
if let Some(temp_type) = &flags.temperature_type {
|
||||||
// Give lowest priority to config.
|
// Give lowest priority to config.
|
||||||
match temp_type.as_str() {
|
return match temp_type.as_str() {
|
||||||
"fahrenheit" | "f" => {
|
"fahrenheit" | "f" => {
|
||||||
return Ok(data_harvester::temperature::TemperatureType::Fahrenheit);
|
Ok(data_harvester::temperature::TemperatureType::Fahrenheit)
|
||||||
}
|
}
|
||||||
"kelvin" | "k" => {
|
"kelvin" | "k" => {
|
||||||
return Ok(data_harvester::temperature::TemperatureType::Kelvin);
|
Ok(data_harvester::temperature::TemperatureType::Kelvin)
|
||||||
}
|
}
|
||||||
"celsius" | "c" => {
|
"celsius" | "c" => {
|
||||||
return Ok(data_harvester::temperature::TemperatureType::Celsius);
|
Ok(data_harvester::temperature::TemperatureType::Celsius)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(BottomError::ConfigError(
|
Err(BottomError::ConfigError(
|
||||||
"Invalid temperature type. Please have the value be of the form \
|
"Invalid temperature type. Please have the value be of the form \
|
||||||
<kelvin|k|celsius|c|fahrenheit|f>"
|
<kelvin|k|celsius|c|fahrenheit|f>"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
));
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -717,7 +718,7 @@ fn generate_config_colours(config: &Config, painter: &mut canvas::Painter) -> er
|
||||||
painter.colours.set_avg_cpu_colour(avg_cpu_color)?;
|
painter.colours.set_avg_cpu_colour(avg_cpu_color)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(cpu_core_colors) = &(colours.cpu_core_colors) {
|
if let Some(cpu_core_colors) = &colours.cpu_core_colors {
|
||||||
painter.colours.set_cpu_colours(cpu_core_colors)?;
|
painter.colours.set_cpu_colours(cpu_core_colors)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
use assert_cmd::prelude::*;
|
use assert_cmd::prelude::*;
|
||||||
use predicates::prelude::*;
|
use predicates::prelude::*;
|
||||||
use std::process::Command;
|
|
||||||
|
|
||||||
// These tests are mostly here just to ensure that invalid results will be caught when passing arguments...
|
// These tests are mostly here just to ensure that invalid results will be caught when passing arguments...
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue