mirror of
https://github.com/ClementTsang/bottom
synced 2024-11-27 14:40:43 +00:00
feature: add the ability to configure the disk widget's table columns (#1625)
* a bit of refactoring here... * some refactoring, add columns * cleanup * add disk column feature * update changelog
This commit is contained in:
parent
c8cba49463
commit
196d6d18c6
21 changed files with 413 additions and 154 deletions
|
@ -22,6 +22,10 @@ That said, these are more guidelines rather than hardset rules, though the proje
|
||||||
|
|
||||||
## [0.11.0] - Unreleased
|
## [0.11.0] - Unreleased
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- [#1625](https://github.com/ClementTsang/bottom/pull/1625): Add the ability to configure the disk widget's table columns.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
- [#1551](https://github.com/ClementTsang/bottom/pull/1551): Fix missing parent section names in default config.
|
- [#1551](https://github.com/ClementTsang/bottom/pull/1551): Fix missing parent section names in default config.
|
||||||
|
|
|
@ -8,24 +8,34 @@
|
||||||
[flags]
|
[flags]
|
||||||
# Whether to hide the average cpu entry.
|
# Whether to hide the average cpu entry.
|
||||||
#hide_avg_cpu = false
|
#hide_avg_cpu = false
|
||||||
|
|
||||||
# Whether to use dot markers rather than braille.
|
# Whether to use dot markers rather than braille.
|
||||||
#dot_marker = false
|
#dot_marker = false
|
||||||
|
|
||||||
# The update rate of the application.
|
# The update rate of the application.
|
||||||
#rate = "1s"
|
#rate = "1s"
|
||||||
|
|
||||||
# Whether to put the CPU legend to the left.
|
# Whether to put the CPU legend to the left.
|
||||||
#cpu_left_legend = false
|
#cpu_left_legend = false
|
||||||
|
|
||||||
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
|
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
|
||||||
#current_usage = false
|
#current_usage = false
|
||||||
|
|
||||||
# Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus).
|
# Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus).
|
||||||
#unnormalized_cpu = false
|
#unnormalized_cpu = false
|
||||||
|
|
||||||
# Whether to group processes with the same name together by default.
|
# Whether to group processes with the same name together by default.
|
||||||
#group_processes = false
|
#group_processes = false
|
||||||
|
|
||||||
# Whether to make process searching case sensitive by default.
|
# Whether to make process searching case sensitive by default.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to make process searching look for matching the entire word by default.
|
# Whether to make process searching look for matching the entire word by default.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
# Whether to make process searching use regex by default.
|
# Whether to make process searching use regex by default.
|
||||||
#regex = false
|
#regex = false
|
||||||
|
|
||||||
# The temperature unit. One of the following, defaults to "c" for Celsius:
|
# The temperature unit. One of the following, defaults to "c" for Celsius:
|
||||||
#temperature_type = "c"
|
#temperature_type = "c"
|
||||||
##temperature_type = "k"
|
##temperature_type = "k"
|
||||||
|
@ -33,79 +43,112 @@
|
||||||
##temperature_type = "kelvin"
|
##temperature_type = "kelvin"
|
||||||
##temperature_type = "fahrenheit"
|
##temperature_type = "fahrenheit"
|
||||||
##temperature_type = "celsius"
|
##temperature_type = "celsius"
|
||||||
|
|
||||||
# The default time interval (in milliseconds).
|
# The default time interval (in milliseconds).
|
||||||
#default_time_value = "60s"
|
#default_time_value = "60s"
|
||||||
|
|
||||||
# The time delta on each zoom in/out action (in milliseconds).
|
# The time delta on each zoom in/out action (in milliseconds).
|
||||||
#time_delta = 15000
|
#time_delta = 15000
|
||||||
|
|
||||||
# Hides the time scale.
|
# Hides the time scale.
|
||||||
#hide_time = false
|
#hide_time = false
|
||||||
|
|
||||||
# Override layout default widget
|
# Override layout default widget
|
||||||
#default_widget_type = "proc"
|
#default_widget_type = "proc"
|
||||||
#default_widget_count = 1
|
#default_widget_count = 1
|
||||||
|
|
||||||
# Expand selected widget upon starting the app
|
# Expand selected widget upon starting the app
|
||||||
#expanded = true
|
#expanded = true
|
||||||
|
|
||||||
# Use basic mode
|
# Use basic mode
|
||||||
#basic = false
|
#basic = false
|
||||||
|
|
||||||
# Use the old network legend style
|
# Use the old network legend style
|
||||||
#use_old_network_legend = false
|
#use_old_network_legend = false
|
||||||
|
|
||||||
# Remove space in tables
|
# Remove space in tables
|
||||||
#hide_table_gap = false
|
#hide_table_gap = false
|
||||||
|
|
||||||
# Show the battery widgets
|
# Show the battery widgets
|
||||||
#battery = false
|
#battery = false
|
||||||
|
|
||||||
# Disable mouse clicks
|
# Disable mouse clicks
|
||||||
#disable_click = false
|
#disable_click = false
|
||||||
|
|
||||||
# Show memory values in the processes widget as values by default
|
# Show memory values in the processes widget as values by default
|
||||||
#process_memory_as_value = false
|
#process_memory_as_value = false
|
||||||
|
|
||||||
# Show tree mode by default in the processes widget.
|
# Show tree mode by default in the processes widget.
|
||||||
#tree = false
|
#tree = false
|
||||||
|
|
||||||
# Shows an indicator in table widgets tracking where in the list you are.
|
# Shows an indicator in table widgets tracking where in the list you are.
|
||||||
#show_table_scroll_position = false
|
#show_table_scroll_position = false
|
||||||
|
|
||||||
# Show processes as their commands by default in the process widget.
|
# Show processes as their commands by default in the process widget.
|
||||||
#process_command = false
|
#process_command = false
|
||||||
|
|
||||||
# Displays the network widget with binary prefixes.
|
# Displays the network widget with binary prefixes.
|
||||||
#network_use_binary_prefix = false
|
#network_use_binary_prefix = false
|
||||||
|
|
||||||
# Displays the network widget using bytes.
|
# Displays the network widget using bytes.
|
||||||
#network_use_bytes = false
|
#network_use_bytes = false
|
||||||
|
|
||||||
# Displays the network widget with a log scale.
|
# Displays the network widget with a log scale.
|
||||||
#network_use_log = false
|
#network_use_log = false
|
||||||
|
|
||||||
# Hides advanced options to stop a process on Unix-like systems.
|
# Hides advanced options to stop a process on Unix-like systems.
|
||||||
#disable_advanced_kill = false
|
#disable_advanced_kill = false
|
||||||
|
|
||||||
# Hide GPU(s) information
|
# Hide GPU(s) information
|
||||||
#disable_gpu = false
|
#disable_gpu = false
|
||||||
|
|
||||||
# Shows cache and buffer memory
|
# Shows cache and buffer memory
|
||||||
#enable_cache_memory = false
|
#enable_cache_memory = false
|
||||||
|
|
||||||
# How much data is stored at once in terms of time.
|
# How much data is stored at once in terms of time.
|
||||||
#retention = "10m"
|
#retention = "10m"
|
||||||
|
|
||||||
# Where to place the legend for the memory widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
# Where to place the legend for the memory widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
||||||
#memory_legend = "top-right"
|
#memory_legend = "top-right"
|
||||||
|
|
||||||
# Where to place the legend for the network widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
# Where to place the legend for the network widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
||||||
#network_legend = "top-right"
|
#network_legend = "top-right"
|
||||||
|
|
||||||
|
|
||||||
# Processes widget configuration
|
# Processes widget configuration
|
||||||
#[processes]
|
#[processes]
|
||||||
# The columns shown by the process widget. The following columns are supported (the GPU columns are only available if the GPU feature is enabled when built):
|
# The columns shown by the process widget. The following columns are supported (the GPU columns are only available if the GPU feature is enabled when built):
|
||||||
# PID, Name, CPU%, Mem%, R/s, W/s, T.Read, T.Write, User, State, Time, GMem%, GPU%
|
# PID, Name, CPU%, Mem%, R/s, W/s, T.Read, T.Write, User, State, Time, GMem%, GPU%
|
||||||
#columns = ["PID", "Name", "CPU%", "Mem%", "R/s", "W/s", "T.Read", "T.Write", "User", "State", "GMem%", "GPU%"]
|
#columns = ["PID", "Name", "CPU%", "Mem%", "R/s", "W/s", "T.Read", "T.Write", "User", "State", "GMem%", "GPU%"]
|
||||||
|
|
||||||
|
|
||||||
# CPU widget configuration
|
# CPU widget configuration
|
||||||
#[cpu]
|
#[cpu]
|
||||||
# One of "all" (default), "average"/"avg"
|
# One of "all" (default), "average"/"avg"
|
||||||
# default = "average"
|
#default = "average"
|
||||||
|
|
||||||
|
|
||||||
# Disk widget configuration
|
# Disk widget configuration
|
||||||
#[disk]
|
#[disk]
|
||||||
|
# The columns shown by the process widget. The following columns are supported:
|
||||||
|
# Disk, Mount, Used, Free, Total, Used%, Free%, R/s, W/s
|
||||||
|
#columns = ["Disk", "Mount", "Used", "Free", "Total", "Used%", "R/s", "W/s"]
|
||||||
|
|
||||||
# By default, there are no disk name filters enabled. These can be turned on to filter out specific data entries if you
|
# By default, there are no disk name filters enabled. These can be turned on to filter out specific data entries if you
|
||||||
# don't want to see them. An example use case is provided below.
|
# don't want to see them. An example use case is provided below.
|
||||||
#[disk.name_filter]
|
#[disk.name_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
|
#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
@ -113,51 +156,63 @@
|
||||||
#[disk.mount_filter]
|
#[disk.mount_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["/mnt/.*", "/boot"]
|
#list = ["/mnt/.*", "/boot"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
|
||||||
# Temperature widget configuration
|
# Temperature widget configuration
|
||||||
#[temperature]
|
#[temperature]
|
||||||
|
|
||||||
# By default, there are no temperature sensor filters enabled. An example use case is provided below.
|
# By default, there are no temperature sensor filters enabled. An example use case is provided below.
|
||||||
#[temperature.sensor_filter]
|
#[temperature.sensor_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["cpu", "wifi"]
|
#list = ["cpu", "wifi"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = false
|
#regex = false
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
|
||||||
# Network widget configuration
|
# Network widget configuration
|
||||||
#[network]
|
#[network]
|
||||||
|
|
||||||
# By default, there are no network interface filters enabled. An example use case is provided below.
|
# By default, there are no network interface filters enabled. An example use case is provided below.
|
||||||
#[network.interface_filter]
|
#[network.interface_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["virbr0.*"]
|
#list = ["virbr0.*"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
|
||||||
# These are all the components that support custom theming. Note that colour support
|
# These are all the components that support custom theming. Note that colour support
|
||||||
# will depend on terminal support.
|
# will depend on terminal support.
|
||||||
#[styles] # Uncomment if you want to use custom styling
|
#[styles] # Uncomment if you want to use custom styling
|
||||||
|
|
||||||
# Built-in themes. Valid values are:
|
# Built-in themes. Valid values are:
|
||||||
# - "default"
|
# - "default"
|
||||||
# - "default-light"
|
# - "default-light"
|
||||||
|
|
|
@ -183,10 +183,35 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"DiskColumn": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"Disk",
|
||||||
|
"Free",
|
||||||
|
"Free%",
|
||||||
|
"Mount",
|
||||||
|
"R/s",
|
||||||
|
"Read",
|
||||||
|
"Rps",
|
||||||
|
"Total",
|
||||||
|
"Used",
|
||||||
|
"Used%",
|
||||||
|
"W/s",
|
||||||
|
"Wps",
|
||||||
|
"Write"
|
||||||
|
]
|
||||||
|
},
|
||||||
"DiskConfig": {
|
"DiskConfig": {
|
||||||
"description": "Disk configuration.",
|
"description": "Disk configuration.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"columns": {
|
||||||
|
"description": "A list of disk widget columns.",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/DiskColumn"
|
||||||
|
}
|
||||||
|
},
|
||||||
"mount_filter": {
|
"mount_filter": {
|
||||||
"description": "A filter over the mount names.",
|
"description": "A filter over the mount names.",
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
|
|
|
@ -51,7 +51,7 @@ def main():
|
||||||
read_file = re.sub(
|
read_file = re.sub(
|
||||||
r"^#(\s\s+)([a-zA-Z\[])", r"\2", read_file, flags=re.MULTILINE
|
r"^#(\s\s+)([a-zA-Z\[])", r"\2", read_file, flags=re.MULTILINE
|
||||||
)
|
)
|
||||||
print(f"uncommented file: \n{read_file}")
|
print(f"uncommented file: \n{read_file}\n=====\n")
|
||||||
|
|
||||||
toml_str = tomllib.loads(read_file)
|
toml_str = tomllib.loads(read_file)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -25,7 +25,7 @@ use crate::{
|
||||||
processes::{Pid, ProcessHarvest},
|
processes::{Pid, ProcessHarvest},
|
||||||
temperature, Data,
|
temperature, Data,
|
||||||
},
|
},
|
||||||
utils::data_prefixes::*,
|
dec_bytes_per_second_string,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type TimeOffset = f64;
|
pub type TimeOffset = f64;
|
||||||
|
@ -423,20 +423,11 @@ impl DataCollection {
|
||||||
*io_curr = (r_rate, w_rate);
|
*io_curr = (r_rate, w_rate);
|
||||||
*io_prev = (io_r_pt, io_w_pt);
|
*io_prev = (io_r_pt, io_w_pt);
|
||||||
|
|
||||||
|
// TODO: idk why I'm generating this here tbh
|
||||||
if let Some(io_labels) = self.io_labels.get_mut(itx) {
|
if let Some(io_labels) = self.io_labels.get_mut(itx) {
|
||||||
let converted_read = get_decimal_bytes(r_rate);
|
|
||||||
let converted_write = get_decimal_bytes(w_rate);
|
|
||||||
*io_labels = (
|
*io_labels = (
|
||||||
if r_rate >= GIGA_LIMIT {
|
dec_bytes_per_second_string(r_rate),
|
||||||
format!("{:.*}{}/s", 1, converted_read.0, converted_read.1)
|
dec_bytes_per_second_string(w_rate),
|
||||||
} else {
|
|
||||||
format!("{:.*}{}/s", 0, converted_read.0, converted_read.1)
|
|
||||||
},
|
|
||||||
if w_rate >= GIGA_LIMIT {
|
|
||||||
format!("{:.*}{}/s", 1, converted_write.0, converted_write.1)
|
|
||||||
} else {
|
|
||||||
format!("{:.*}{}/s", 0, converted_write.0, converted_write.1)
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,16 @@ impl SortOrder {
|
||||||
SortOrder::Descending => SortOrder::Ascending,
|
SortOrder::Descending => SortOrder::Ascending,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A hack to get a const default.
|
||||||
|
pub const fn const_default() -> Self {
|
||||||
|
Self::Ascending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for SortOrder {
|
impl Default for SortOrder {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Ascending
|
Self::const_default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,18 +200,18 @@ where
|
||||||
|
|
||||||
/// Creates a new [`SortColumn`] with a hard width, which has no shortcut
|
/// Creates a new [`SortColumn`] with a hard width, which has no shortcut
|
||||||
/// and sorts by default in ascending order ([`SortOrder::Ascending`]).
|
/// and sorts by default in ascending order ([`SortOrder::Ascending`]).
|
||||||
pub fn hard(inner: T, width: u16) -> Self {
|
pub const fn hard(inner: T, width: u16) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
bounds: ColumnWidthBounds::Hard(width),
|
bounds: ColumnWidthBounds::Hard(width),
|
||||||
is_hidden: false,
|
is_hidden: false,
|
||||||
default_order: SortOrder::default(),
|
default_order: SortOrder::const_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`SortColumn`] with a soft width, which has no shortcut
|
/// Creates a new [`SortColumn`] with a soft width, which has no shortcut
|
||||||
/// and sorts by default in ascending order ([`SortOrder::Ascending`]).
|
/// and sorts by default in ascending order ([`SortOrder::Ascending`]).
|
||||||
pub fn soft(inner: T, max_percentage: Option<f32>) -> Self {
|
pub const fn soft(inner: T, max_percentage: Option<f32>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
bounds: ColumnWidthBounds::Soft {
|
bounds: ColumnWidthBounds::Soft {
|
||||||
|
@ -214,7 +219,7 @@ where
|
||||||
max_percentage,
|
max_percentage,
|
||||||
},
|
},
|
||||||
is_hidden: false,
|
is_hidden: false,
|
||||||
default_order: SortOrder::default(),
|
default_order: SortOrder::const_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +230,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the default sort order to [`SortOrder::Descending`].
|
/// Sets the default sort order to [`SortOrder::Descending`].
|
||||||
pub fn default_descending(mut self) -> Self {
|
pub const fn default_descending(mut self) -> Self {
|
||||||
self.default_order = SortOrder::Descending;
|
self.default_order = SortOrder::Descending;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
|
@ -268,24 +268,34 @@ pub(crate) const CONFIG_TEXT: &str = r#"# This is a default config file for bott
|
||||||
[flags]
|
[flags]
|
||||||
# Whether to hide the average cpu entry.
|
# Whether to hide the average cpu entry.
|
||||||
#hide_avg_cpu = false
|
#hide_avg_cpu = false
|
||||||
|
|
||||||
# Whether to use dot markers rather than braille.
|
# Whether to use dot markers rather than braille.
|
||||||
#dot_marker = false
|
#dot_marker = false
|
||||||
|
|
||||||
# The update rate of the application.
|
# The update rate of the application.
|
||||||
#rate = "1s"
|
#rate = "1s"
|
||||||
|
|
||||||
# Whether to put the CPU legend to the left.
|
# Whether to put the CPU legend to the left.
|
||||||
#cpu_left_legend = false
|
#cpu_left_legend = false
|
||||||
|
|
||||||
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
|
# Whether to set CPU% on a process to be based on the total CPU or just current usage.
|
||||||
#current_usage = false
|
#current_usage = false
|
||||||
|
|
||||||
# Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus).
|
# Whether to set CPU% on a process to be based on the total CPU or per-core CPU% (not divided by the number of cpus).
|
||||||
#unnormalized_cpu = false
|
#unnormalized_cpu = false
|
||||||
|
|
||||||
# Whether to group processes with the same name together by default.
|
# Whether to group processes with the same name together by default.
|
||||||
#group_processes = false
|
#group_processes = false
|
||||||
|
|
||||||
# Whether to make process searching case sensitive by default.
|
# Whether to make process searching case sensitive by default.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to make process searching look for matching the entire word by default.
|
# Whether to make process searching look for matching the entire word by default.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
# Whether to make process searching use regex by default.
|
# Whether to make process searching use regex by default.
|
||||||
#regex = false
|
#regex = false
|
||||||
|
|
||||||
# The temperature unit. One of the following, defaults to "c" for Celsius:
|
# The temperature unit. One of the following, defaults to "c" for Celsius:
|
||||||
#temperature_type = "c"
|
#temperature_type = "c"
|
||||||
##temperature_type = "k"
|
##temperature_type = "k"
|
||||||
|
@ -293,79 +303,112 @@ pub(crate) const CONFIG_TEXT: &str = r#"# This is a default config file for bott
|
||||||
##temperature_type = "kelvin"
|
##temperature_type = "kelvin"
|
||||||
##temperature_type = "fahrenheit"
|
##temperature_type = "fahrenheit"
|
||||||
##temperature_type = "celsius"
|
##temperature_type = "celsius"
|
||||||
|
|
||||||
# The default time interval (in milliseconds).
|
# The default time interval (in milliseconds).
|
||||||
#default_time_value = "60s"
|
#default_time_value = "60s"
|
||||||
|
|
||||||
# The time delta on each zoom in/out action (in milliseconds).
|
# The time delta on each zoom in/out action (in milliseconds).
|
||||||
#time_delta = 15000
|
#time_delta = 15000
|
||||||
|
|
||||||
# Hides the time scale.
|
# Hides the time scale.
|
||||||
#hide_time = false
|
#hide_time = false
|
||||||
|
|
||||||
# Override layout default widget
|
# Override layout default widget
|
||||||
#default_widget_type = "proc"
|
#default_widget_type = "proc"
|
||||||
#default_widget_count = 1
|
#default_widget_count = 1
|
||||||
|
|
||||||
# Expand selected widget upon starting the app
|
# Expand selected widget upon starting the app
|
||||||
#expanded = true
|
#expanded = true
|
||||||
|
|
||||||
# Use basic mode
|
# Use basic mode
|
||||||
#basic = false
|
#basic = false
|
||||||
|
|
||||||
# Use the old network legend style
|
# Use the old network legend style
|
||||||
#use_old_network_legend = false
|
#use_old_network_legend = false
|
||||||
|
|
||||||
# Remove space in tables
|
# Remove space in tables
|
||||||
#hide_table_gap = false
|
#hide_table_gap = false
|
||||||
|
|
||||||
# Show the battery widgets
|
# Show the battery widgets
|
||||||
#battery = false
|
#battery = false
|
||||||
|
|
||||||
# Disable mouse clicks
|
# Disable mouse clicks
|
||||||
#disable_click = false
|
#disable_click = false
|
||||||
|
|
||||||
# Show memory values in the processes widget as values by default
|
# Show memory values in the processes widget as values by default
|
||||||
#process_memory_as_value = false
|
#process_memory_as_value = false
|
||||||
|
|
||||||
# Show tree mode by default in the processes widget.
|
# Show tree mode by default in the processes widget.
|
||||||
#tree = false
|
#tree = false
|
||||||
|
|
||||||
# Shows an indicator in table widgets tracking where in the list you are.
|
# Shows an indicator in table widgets tracking where in the list you are.
|
||||||
#show_table_scroll_position = false
|
#show_table_scroll_position = false
|
||||||
|
|
||||||
# Show processes as their commands by default in the process widget.
|
# Show processes as their commands by default in the process widget.
|
||||||
#process_command = false
|
#process_command = false
|
||||||
|
|
||||||
# Displays the network widget with binary prefixes.
|
# Displays the network widget with binary prefixes.
|
||||||
#network_use_binary_prefix = false
|
#network_use_binary_prefix = false
|
||||||
|
|
||||||
# Displays the network widget using bytes.
|
# Displays the network widget using bytes.
|
||||||
#network_use_bytes = false
|
#network_use_bytes = false
|
||||||
|
|
||||||
# Displays the network widget with a log scale.
|
# Displays the network widget with a log scale.
|
||||||
#network_use_log = false
|
#network_use_log = false
|
||||||
|
|
||||||
# Hides advanced options to stop a process on Unix-like systems.
|
# Hides advanced options to stop a process on Unix-like systems.
|
||||||
#disable_advanced_kill = false
|
#disable_advanced_kill = false
|
||||||
|
|
||||||
# Hide GPU(s) information
|
# Hide GPU(s) information
|
||||||
#disable_gpu = false
|
#disable_gpu = false
|
||||||
|
|
||||||
# Shows cache and buffer memory
|
# Shows cache and buffer memory
|
||||||
#enable_cache_memory = false
|
#enable_cache_memory = false
|
||||||
|
|
||||||
# How much data is stored at once in terms of time.
|
# How much data is stored at once in terms of time.
|
||||||
#retention = "10m"
|
#retention = "10m"
|
||||||
|
|
||||||
# Where to place the legend for the memory widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
# Where to place the legend for the memory widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
||||||
#memory_legend = "top-right"
|
#memory_legend = "top-right"
|
||||||
|
|
||||||
# Where to place the legend for the network widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
# Where to place the legend for the network widget. One of "none", "top-left", "top", "top-right", "left", "right", "bottom-left", "bottom", "bottom-right".
|
||||||
#network_legend = "top-right"
|
#network_legend = "top-right"
|
||||||
|
|
||||||
|
|
||||||
# Processes widget configuration
|
# Processes widget configuration
|
||||||
#[processes]
|
#[processes]
|
||||||
# The columns shown by the process widget. The following columns are supported (the GPU columns are only available if the GPU feature is enabled when built):
|
# The columns shown by the process widget. The following columns are supported (the GPU columns are only available if the GPU feature is enabled when built):
|
||||||
# PID, Name, CPU%, Mem%, R/s, W/s, T.Read, T.Write, User, State, Time, GMem%, GPU%
|
# PID, Name, CPU%, Mem%, R/s, W/s, T.Read, T.Write, User, State, Time, GMem%, GPU%
|
||||||
#columns = ["PID", "Name", "CPU%", "Mem%", "R/s", "W/s", "T.Read", "T.Write", "User", "State", "GMem%", "GPU%"]
|
#columns = ["PID", "Name", "CPU%", "Mem%", "R/s", "W/s", "T.Read", "T.Write", "User", "State", "GMem%", "GPU%"]
|
||||||
|
|
||||||
|
|
||||||
# CPU widget configuration
|
# CPU widget configuration
|
||||||
#[cpu]
|
#[cpu]
|
||||||
# One of "all" (default), "average"/"avg"
|
# One of "all" (default), "average"/"avg"
|
||||||
# default = "average"
|
#default = "average"
|
||||||
|
|
||||||
|
|
||||||
# Disk widget configuration
|
# Disk widget configuration
|
||||||
#[disk]
|
#[disk]
|
||||||
|
# The columns shown by the process widget. The following columns are supported:
|
||||||
|
# Disk, Mount, Used, Free, Total, Used%, Free%, R/s, W/s
|
||||||
|
#columns = ["Disk", "Mount", "Used", "Free", "Total", "Used%", "R/s", "W/s"]
|
||||||
|
|
||||||
# By default, there are no disk name filters enabled. These can be turned on to filter out specific data entries if you
|
# By default, there are no disk name filters enabled. These can be turned on to filter out specific data entries if you
|
||||||
# don't want to see them. An example use case is provided below.
|
# don't want to see them. An example use case is provided below.
|
||||||
#[disk.name_filter]
|
#[disk.name_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
|
#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
@ -373,51 +416,63 @@ pub(crate) const CONFIG_TEXT: &str = r#"# This is a default config file for bott
|
||||||
#[disk.mount_filter]
|
#[disk.mount_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["/mnt/.*", "/boot"]
|
#list = ["/mnt/.*", "/boot"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
|
||||||
# Temperature widget configuration
|
# Temperature widget configuration
|
||||||
#[temperature]
|
#[temperature]
|
||||||
|
|
||||||
# By default, there are no temperature sensor filters enabled. An example use case is provided below.
|
# By default, there are no temperature sensor filters enabled. An example use case is provided below.
|
||||||
#[temperature.sensor_filter]
|
#[temperature.sensor_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["cpu", "wifi"]
|
#list = ["cpu", "wifi"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = false
|
#regex = false
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
|
||||||
# Network widget configuration
|
# Network widget configuration
|
||||||
#[network]
|
#[network]
|
||||||
|
|
||||||
# By default, there are no network interface filters enabled. An example use case is provided below.
|
# By default, there are no network interface filters enabled. An example use case is provided below.
|
||||||
#[network.interface_filter]
|
#[network.interface_filter]
|
||||||
# Whether to ignore any matches. Defaults to true.
|
# Whether to ignore any matches. Defaults to true.
|
||||||
#is_list_ignored = true
|
#is_list_ignored = true
|
||||||
|
|
||||||
# A list of filters to try and match.
|
# A list of filters to try and match.
|
||||||
#list = ["virbr0.*"]
|
#list = ["virbr0.*"]
|
||||||
|
|
||||||
# Whether to use regex. Defaults to false.
|
# Whether to use regex. Defaults to false.
|
||||||
#regex = true
|
#regex = true
|
||||||
|
|
||||||
# Whether to be case-sensitive. Defaults to false.
|
# Whether to be case-sensitive. Defaults to false.
|
||||||
#case_sensitive = false
|
#case_sensitive = false
|
||||||
|
|
||||||
# Whether to be require matching the whole word. Defaults to false.
|
# Whether to be require matching the whole word. Defaults to false.
|
||||||
#whole_word = false
|
#whole_word = false
|
||||||
|
|
||||||
|
|
||||||
# These are all the components that support custom theming. Note that colour support
|
# These are all the components that support custom theming. Note that colour support
|
||||||
# will depend on terminal support.
|
# will depend on terminal support.
|
||||||
#[styles] # Uncomment if you want to use custom styling
|
#[styles] # Uncomment if you want to use custom styling
|
||||||
|
|
||||||
# Built-in themes. Valid values are:
|
# Built-in themes. Valid values are:
|
||||||
# - "default"
|
# - "default"
|
||||||
# - "default-light"
|
# - "default-light"
|
||||||
|
|
|
@ -402,15 +402,15 @@ pub fn convert_network_points(
|
||||||
};
|
};
|
||||||
|
|
||||||
if need_four_points {
|
if need_four_points {
|
||||||
let rx_display = format!("{:.*}{}", 1, rx_converted_result.0, rx_converted_result.1);
|
let rx_display = format!("{:.1}{}", rx_converted_result.0, rx_converted_result.1);
|
||||||
let total_rx_display = Some(format!(
|
let total_rx_display = Some(format!(
|
||||||
"{:.*}{}",
|
"{:.1}{}",
|
||||||
1, total_rx_converted_result.0, total_rx_converted_result.1
|
total_rx_converted_result.0, total_rx_converted_result.1
|
||||||
));
|
));
|
||||||
let tx_display = format!("{:.*}{}", 1, tx_converted_result.0, tx_converted_result.1);
|
let tx_display = format!("{:.1}{}", tx_converted_result.0, tx_converted_result.1);
|
||||||
let total_tx_display = Some(format!(
|
let total_tx_display = Some(format!(
|
||||||
"{:.*}{}",
|
"{:.1}{}",
|
||||||
1, total_tx_converted_result.0, total_tx_converted_result.1
|
total_tx_converted_result.0, total_tx_converted_result.1
|
||||||
));
|
));
|
||||||
ConvertedNetworkData {
|
ConvertedNetworkData {
|
||||||
rx,
|
rx,
|
||||||
|
@ -474,35 +474,38 @@ pub fn convert_network_points(
|
||||||
/// Returns a string given a value that is converted to the closest binary
|
/// Returns a string given a value that is converted to the closest binary
|
||||||
/// variant. If the value is greater than a gibibyte, then it will return a
|
/// variant. If the value is greater than a gibibyte, then it will return a
|
||||||
/// decimal place.
|
/// decimal place.
|
||||||
|
#[inline]
|
||||||
pub fn binary_byte_string(value: u64) -> String {
|
pub fn binary_byte_string(value: u64) -> String {
|
||||||
let converted_values = get_binary_bytes(value);
|
let converted_values = get_binary_bytes(value);
|
||||||
if value >= GIBI_LIMIT {
|
if value >= GIBI_LIMIT {
|
||||||
format!("{:.*}{}", 1, converted_values.0, converted_values.1)
|
format!("{:.1}{}", converted_values.0, converted_values.1)
|
||||||
} else {
|
} else {
|
||||||
format!("{:.*}{}", 0, converted_values.0, converted_values.1)
|
format!("{:.0}{}", converted_values.0, converted_values.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a string given a value that is converted to the closest SI-variant.
|
/// Returns a string given a value that is converted to the closest SI-variant.
|
||||||
/// If the value is greater than a giga-X, then it will return a decimal place.
|
/// If the value is greater than a giga-X, then it will return a decimal place.
|
||||||
|
#[inline]
|
||||||
pub fn dec_bytes_per_string(value: u64) -> String {
|
pub fn dec_bytes_per_string(value: u64) -> String {
|
||||||
let converted_values = get_decimal_bytes(value);
|
let converted_values = get_decimal_bytes(value);
|
||||||
if value >= GIGA_LIMIT {
|
if value >= GIGA_LIMIT {
|
||||||
format!("{:.*}{}", 1, converted_values.0, converted_values.1)
|
format!("{:.1}{}", converted_values.0, converted_values.1)
|
||||||
} else {
|
} else {
|
||||||
format!("{:.*}{}", 0, converted_values.0, converted_values.1)
|
format!("{:.0}{}", converted_values.0, converted_values.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a string given a value that is converted to the closest SI-variant,
|
/// Returns a string given a value that is converted to the closest SI-variant,
|
||||||
/// per second. If the value is greater than a giga-X, then it will return a
|
/// per second. If the value is greater than a giga-X, then it will return a
|
||||||
/// decimal place.
|
/// decimal place.
|
||||||
|
#[inline]
|
||||||
pub fn dec_bytes_per_second_string(value: u64) -> String {
|
pub fn dec_bytes_per_second_string(value: u64) -> String {
|
||||||
let converted_values = get_decimal_bytes(value);
|
let converted_values = get_decimal_bytes(value);
|
||||||
if value >= GIGA_LIMIT {
|
if value >= GIGA_LIMIT {
|
||||||
format!("{:.*}{}/s", 1, converted_values.0, converted_values.1)
|
format!("{:.1}{}/s", converted_values.0, converted_values.1)
|
||||||
} else {
|
} else {
|
||||||
format!("{:.*}{}/s", 0, converted_values.0, converted_values.1)
|
format!("{:.0}{}/s", converted_values.0, converted_values.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,9 +514,9 @@ pub fn dec_bytes_per_second_string(value: u64) -> String {
|
||||||
pub fn dec_bytes_string(value: u64) -> String {
|
pub fn dec_bytes_string(value: u64) -> String {
|
||||||
let converted_values = get_decimal_bytes(value);
|
let converted_values = get_decimal_bytes(value);
|
||||||
if value >= GIGA_LIMIT {
|
if value >= GIGA_LIMIT {
|
||||||
format!("{:.*}{}", 1, converted_values.0, converted_values.1)
|
format!("{:.1}{}", converted_values.0, converted_values.1)
|
||||||
} else {
|
} else {
|
||||||
format!("{:.*}{}", 0, converted_values.0, converted_values.1)
|
format!("{:.0}{}", converted_values.0, converted_values.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -279,6 +279,8 @@ fn generate_schema() -> anyhow::Result<()> {
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use strum::VariantArray;
|
use strum::VariantArray;
|
||||||
|
|
||||||
|
// TODO: Maybe make this case insensitive? See https://stackoverflow.com/a/68639341
|
||||||
|
|
||||||
let proc_columns = schema.definitions.get_mut("ProcColumn").unwrap();
|
let proc_columns = schema.definitions.get_mut("ProcColumn").unwrap();
|
||||||
match proc_columns {
|
match proc_columns {
|
||||||
schemars::schema::Schema::Object(proc_columns) => {
|
schemars::schema::Schema::Object(proc_columns) => {
|
||||||
|
@ -293,6 +295,21 @@ fn generate_schema() -> anyhow::Result<()> {
|
||||||
}
|
}
|
||||||
_ => anyhow::bail!("missing proc columns definition"),
|
_ => anyhow::bail!("missing proc columns definition"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let disk_columns = schema.definitions.get_mut("DiskColumn").unwrap();
|
||||||
|
match disk_columns {
|
||||||
|
schemars::schema::Schema::Object(disk_columns) => {
|
||||||
|
let enums = disk_columns.enum_values.as_mut().unwrap();
|
||||||
|
*enums = widgets::DiskColumn::VARIANTS
|
||||||
|
.iter()
|
||||||
|
.flat_map(|var| var.get_schema_names())
|
||||||
|
.sorted()
|
||||||
|
.map(|v| serde_json::Value::String(v.to_string()))
|
||||||
|
.dedup()
|
||||||
|
.collect();
|
||||||
|
}
|
||||||
|
_ => anyhow::bail!("missing disk columns definition"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = schema.schema.metadata.as_mut().unwrap();
|
let metadata = schema.schema.metadata.as_mut().unwrap();
|
||||||
|
|
|
@ -403,7 +403,11 @@ pub(crate) fn init_app(
|
||||||
Disk => {
|
Disk => {
|
||||||
disk_state_map.insert(
|
disk_state_map.insert(
|
||||||
widget.widget_id,
|
widget.widget_id,
|
||||||
DiskTableWidget::new(&app_config_fields, &styling),
|
DiskTableWidget::new(
|
||||||
|
&app_config_fields,
|
||||||
|
&styling,
|
||||||
|
config.disk.as_ref().map(|cfg| cfg.columns.as_slice()),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Temp => {
|
Temp => {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::Deserialize;
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
#[cfg_attr(test, derive(PartialEq, Eq))]
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
pub enum CpuDefault {
|
pub(crate) enum CpuDefault {
|
||||||
#[default]
|
#[default]
|
||||||
All,
|
All,
|
||||||
#[serde(alias = "avg")]
|
#[serde(alias = "avg")]
|
||||||
|
@ -17,9 +17,9 @@ pub enum CpuDefault {
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||||
pub struct CpuConfig {
|
pub(crate) struct CpuConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub default: CpuDefault,
|
pub(crate) default: CpuDefault,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -1,15 +1,45 @@
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::options::DiskColumn;
|
||||||
|
|
||||||
use super::IgnoreList;
|
use super::IgnoreList;
|
||||||
|
|
||||||
/// Disk configuration.
|
/// Disk configuration.
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||||
pub struct DiskConfig {
|
pub(crate) struct DiskConfig {
|
||||||
/// A filter over the disk names.
|
/// A filter over the disk names.
|
||||||
pub name_filter: Option<IgnoreList>,
|
pub(crate) name_filter: Option<IgnoreList>,
|
||||||
|
|
||||||
/// A filter over the mount names.
|
/// A filter over the mount names.
|
||||||
pub mount_filter: Option<IgnoreList>,
|
pub(crate) mount_filter: Option<IgnoreList>,
|
||||||
|
|
||||||
|
/// A list of disk widget columns.
|
||||||
|
#[serde(default)]
|
||||||
|
pub(crate) columns: Vec<DiskColumn>, // TODO: make this more composable(?) in the future, we might need to rethink how it's done for custom widgets
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::DiskConfig;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_column_setting() {
|
||||||
|
let config = "";
|
||||||
|
let generated: DiskConfig = toml_edit::de::from_str(config).unwrap();
|
||||||
|
assert!(generated.columns.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_disk_column_settings() {
|
||||||
|
let config = r#"columns = ["disk", "mount", "used", "free", "total", "used%", "free%", "r/s", "w/s"]"#;
|
||||||
|
toml_edit::de::from_str::<DiskConfig>(config).expect("Should succeed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn bad_disk_column_settings() {
|
||||||
|
let config = r#"columns = ["diskk"]"#;
|
||||||
|
toml_edit::de::from_str::<DiskConfig>(config).expect_err("Should error out!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use super::IgnoreList;
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||||
pub struct NetworkConfig {
|
pub(crate) struct NetworkConfig {
|
||||||
/// A filter over the network interface names.
|
/// A filter over the network interface names.
|
||||||
pub interface_filter: Option<IgnoreList>,
|
pub(crate) interface_filter: Option<IgnoreList>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,10 @@ use crate::widgets::ProcColumn;
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||||
pub struct ProcessesConfig {
|
pub(crate) struct ProcessesConfig {
|
||||||
/// A list of process widget columns.
|
/// A list of process widget columns.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub columns: Vec<ProcColumn>,
|
pub(crate) columns: Vec<ProcColumn>, // TODO: make this more composable(?) in the future, we might need to rethink how it's done for custom widgets
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -32,7 +32,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn process_column_settings() {
|
fn valid_process_column_config() {
|
||||||
let config = r#"
|
let config = r#"
|
||||||
columns = ["CPU%", "PiD", "user", "MEM", "Tread", "T.Write", "Rps", "W/s", "tiMe", "USER", "state"]
|
columns = ["CPU%", "PiD", "user", "MEM", "Tread", "T.Write", "Rps", "W/s", "tiMe", "USER", "state"]
|
||||||
"#;
|
"#;
|
||||||
|
@ -57,13 +57,13 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn process_column_settings_2() {
|
fn bad_process_column_config() {
|
||||||
let config = r#"columns = ["MEM", "TWrite", "Cpuz", "read", "wps"]"#;
|
let config = r#"columns = ["MEM", "TWrite", "Cpuz", "read", "wps"]"#;
|
||||||
toml_edit::de::from_str::<ProcessesConfig>(config).expect_err("Should error out!");
|
toml_edit::de::from_str::<ProcessesConfig>(config).expect_err("Should error out!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn process_column_settings_3() {
|
fn valid_process_column_config_2() {
|
||||||
let config = r#"columns = ["Twrite", "T.Write"]"#;
|
let config = r#"columns = ["Twrite", "T.Write"]"#;
|
||||||
let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
|
let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -6,7 +6,7 @@ use super::IgnoreList;
|
||||||
#[derive(Clone, Debug, Default, Deserialize)]
|
#[derive(Clone, Debug, Default, Deserialize)]
|
||||||
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
#[cfg_attr(feature = "generate_schema", derive(schemars::JsonSchema))]
|
||||||
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
#[cfg_attr(test, serde(deny_unknown_fields), derive(PartialEq, Eq))]
|
||||||
pub struct TempConfig {
|
pub(crate) struct TempConfig {
|
||||||
/// A filter over the sensor names.
|
/// A filter over the sensor names.
|
||||||
pub sensor_filter: Option<IgnoreList>,
|
pub(crate) sensor_filter: Option<IgnoreList>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -165,7 +165,7 @@ pub struct CpuWidgetState {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CpuWidgetState {
|
impl CpuWidgetState {
|
||||||
pub fn new(
|
pub(crate) fn new(
|
||||||
config: &AppConfigFields, default_selection: CpuDefault, current_display_time: u64,
|
config: &AppConfigFields, default_selection: CpuDefault, current_display_time: u64,
|
||||||
autohide_timer: Option<Instant>, colours: &ColourPalette,
|
autohide_timer: Option<Instant>, colours: &ColourPalette,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::{borrow::Cow, cmp::max, num::NonZeroU16};
|
use std::{borrow::Cow, cmp::max, num::NonZeroU16};
|
||||||
|
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::AppConfigFields,
|
app::AppConfigFields,
|
||||||
canvas::components::data_table::{
|
canvas::components::data_table::{
|
||||||
|
@ -26,11 +28,7 @@ impl DiskWidgetData {
|
||||||
fn total_space(&self) -> Cow<'static, str> {
|
fn total_space(&self) -> Cow<'static, str> {
|
||||||
if let Some(total_bytes) = self.total_bytes {
|
if let Some(total_bytes) = self.total_bytes {
|
||||||
let converted_total_space = get_decimal_bytes(total_bytes);
|
let converted_total_space = get_decimal_bytes(total_bytes);
|
||||||
format!(
|
format!("{:.0}{}", converted_total_space.0, converted_total_space.1).into()
|
||||||
"{:.*}{}",
|
|
||||||
0, converted_total_space.0, converted_total_space.1
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
} else {
|
} else {
|
||||||
"N/A".into()
|
"N/A".into()
|
||||||
}
|
}
|
||||||
|
@ -39,7 +37,7 @@ impl DiskWidgetData {
|
||||||
fn free_space(&self) -> Cow<'static, str> {
|
fn free_space(&self) -> Cow<'static, str> {
|
||||||
if let Some(free_bytes) = self.free_bytes {
|
if let Some(free_bytes) = self.free_bytes {
|
||||||
let converted_free_space = get_decimal_bytes(free_bytes);
|
let converted_free_space = get_decimal_bytes(free_bytes);
|
||||||
format!("{:.*}{}", 0, converted_free_space.0, converted_free_space.1).into()
|
format!("{:.0}{}", converted_free_space.0, converted_free_space.1).into()
|
||||||
} else {
|
} else {
|
||||||
"N/A".into()
|
"N/A".into()
|
||||||
}
|
}
|
||||||
|
@ -48,7 +46,7 @@ impl DiskWidgetData {
|
||||||
fn used_space(&self) -> Cow<'static, str> {
|
fn used_space(&self) -> Cow<'static, str> {
|
||||||
if let Some(used_bytes) = self.used_bytes {
|
if let Some(used_bytes) = self.used_bytes {
|
||||||
let converted_free_space = get_decimal_bytes(used_bytes);
|
let converted_free_space = get_decimal_bytes(used_bytes);
|
||||||
format!("{:.*}{}", 0, converted_free_space.0, converted_free_space.1).into()
|
format!("{:.0}{}", converted_free_space.0, converted_free_space.1).into()
|
||||||
} else {
|
} else {
|
||||||
"N/A".into()
|
"N/A".into()
|
||||||
}
|
}
|
||||||
|
@ -58,19 +56,16 @@ impl DiskWidgetData {
|
||||||
if let (Some(free_bytes), Some(summed_total_bytes)) =
|
if let (Some(free_bytes), Some(summed_total_bytes)) =
|
||||||
(self.free_bytes, self.summed_total_bytes)
|
(self.free_bytes, self.summed_total_bytes)
|
||||||
{
|
{
|
||||||
Some(free_bytes as f64 / summed_total_bytes as f64 * 100_f64)
|
if summed_total_bytes > 0 {
|
||||||
|
Some(free_bytes as f64 / summed_total_bytes as f64 * 100_f64)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn free_percent_string(&self) -> Cow<'static, str> {
|
|
||||||
match self.free_percent() {
|
|
||||||
Some(val) => format!("{val:.1}%").into(),
|
|
||||||
None => "N/A".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn used_percent(&self) -> Option<f64> {
|
fn used_percent(&self) -> Option<f64> {
|
||||||
if let (Some(used_bytes), Some(summed_total_bytes)) =
|
if let (Some(used_bytes), Some(summed_total_bytes)) =
|
||||||
(self.used_bytes, self.summed_total_bytes)
|
(self.used_bytes, self.summed_total_bytes)
|
||||||
|
@ -84,16 +79,15 @@ impl DiskWidgetData {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn used_percent_string(&self) -> Cow<'static, str> {
|
|
||||||
match self.used_percent() {
|
|
||||||
Some(val) => format!("{val:.1}%").into(),
|
|
||||||
None => "N/A".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum DiskWidgetColumn {
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "generate_schema",
|
||||||
|
derive(schemars::JsonSchema, strum::VariantArray)
|
||||||
|
)]
|
||||||
|
#[cfg_attr(test, derive(PartialEq, Eq))]
|
||||||
|
pub enum DiskColumn {
|
||||||
Disk,
|
Disk,
|
||||||
Mount,
|
Mount,
|
||||||
Used,
|
Used,
|
||||||
|
@ -105,45 +99,91 @@ pub enum DiskWidgetColumn {
|
||||||
IoWrite,
|
IoWrite,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ColumnHeader for DiskWidgetColumn {
|
impl<'de> Deserialize<'de> for DiskColumn {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let value = String::deserialize(deserializer)?.to_lowercase();
|
||||||
|
match value.as_str() {
|
||||||
|
"disk" => Ok(DiskColumn::Disk),
|
||||||
|
"mount" => Ok(DiskColumn::Mount),
|
||||||
|
"used" => Ok(DiskColumn::Used),
|
||||||
|
"free" => Ok(DiskColumn::Free),
|
||||||
|
"total" => Ok(DiskColumn::Total),
|
||||||
|
"usedpercent" | "used%" => Ok(DiskColumn::UsedPercent),
|
||||||
|
"freepercent" | "free%" => Ok(DiskColumn::FreePercent),
|
||||||
|
"r/s" => Ok(DiskColumn::IoRead),
|
||||||
|
"w/s" => Ok(DiskColumn::IoWrite),
|
||||||
|
_ => Err(serde::de::Error::custom(
|
||||||
|
"doesn't match any disk column name",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskColumn {
|
||||||
|
/// An ugly hack to generate the JSON schema.
|
||||||
|
#[cfg(feature = "generate_schema")]
|
||||||
|
pub fn get_schema_names(&self) -> &[&'static str] {
|
||||||
|
match self {
|
||||||
|
DiskColumn::Disk => &["Disk"],
|
||||||
|
DiskColumn::Mount => &["Mount"],
|
||||||
|
DiskColumn::Used => &["Used"],
|
||||||
|
DiskColumn::Free => &["Free"],
|
||||||
|
DiskColumn::Total => &["Total"],
|
||||||
|
DiskColumn::UsedPercent => &["Used%"],
|
||||||
|
DiskColumn::FreePercent => &["Free%"],
|
||||||
|
DiskColumn::IoRead => &["R/s", "Read", "Rps"],
|
||||||
|
DiskColumn::IoWrite => &["W/s", "Write", "Wps"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColumnHeader for DiskColumn {
|
||||||
fn text(&self) -> Cow<'static, str> {
|
fn text(&self) -> Cow<'static, str> {
|
||||||
match self {
|
match self {
|
||||||
DiskWidgetColumn::Disk => "Disk(d)",
|
DiskColumn::Disk => "Disk(d)",
|
||||||
DiskWidgetColumn::Mount => "Mount(m)",
|
DiskColumn::Mount => "Mount(m)",
|
||||||
DiskWidgetColumn::Used => "Used(u)",
|
DiskColumn::Used => "Used(u)",
|
||||||
DiskWidgetColumn::Free => "Free(n)",
|
DiskColumn::Free => "Free(n)",
|
||||||
DiskWidgetColumn::UsedPercent => "Used%(p)",
|
DiskColumn::Total => "Total(t)",
|
||||||
DiskWidgetColumn::FreePercent => "Free%",
|
DiskColumn::UsedPercent => "Used%(p)",
|
||||||
DiskWidgetColumn::Total => "Total(t)",
|
DiskColumn::FreePercent => "Free%",
|
||||||
DiskWidgetColumn::IoRead => "R/s(r)",
|
DiskColumn::IoRead => "R/s(r)",
|
||||||
DiskWidgetColumn::IoWrite => "W/s(w)",
|
DiskColumn::IoWrite => "W/s(w)",
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataToCell<DiskWidgetColumn> for DiskWidgetData {
|
impl DataToCell<DiskColumn> for DiskWidgetData {
|
||||||
fn to_cell(
|
fn to_cell(
|
||||||
&self, column: &DiskWidgetColumn, _calculated_width: NonZeroU16,
|
&self, column: &DiskColumn, _calculated_width: NonZeroU16,
|
||||||
) -> Option<Cow<'static, str>> {
|
) -> Option<Cow<'static, str>> {
|
||||||
|
fn percent_string(value: Option<f64>) -> Cow<'static, str> {
|
||||||
|
match value {
|
||||||
|
Some(val) => format!("{val:.1}%").into(),
|
||||||
|
None => "N/A".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let text = match column {
|
let text = match column {
|
||||||
DiskWidgetColumn::Disk => self.name.clone(),
|
DiskColumn::Disk => self.name.clone(),
|
||||||
DiskWidgetColumn::Mount => self.mount_point.clone(),
|
DiskColumn::Mount => self.mount_point.clone(),
|
||||||
DiskWidgetColumn::Used => self.used_space(),
|
DiskColumn::Used => self.used_space(),
|
||||||
DiskWidgetColumn::Free => self.free_space(),
|
DiskColumn::Free => self.free_space(),
|
||||||
DiskWidgetColumn::UsedPercent => self.used_percent_string(),
|
DiskColumn::UsedPercent => percent_string(self.used_percent()),
|
||||||
DiskWidgetColumn::FreePercent => self.free_percent_string(),
|
DiskColumn::FreePercent => percent_string(self.free_percent()),
|
||||||
DiskWidgetColumn::Total => self.total_space(),
|
DiskColumn::Total => self.total_space(),
|
||||||
DiskWidgetColumn::IoRead => self.io_read.clone(),
|
DiskColumn::IoRead => self.io_read.clone(),
|
||||||
DiskWidgetColumn::IoWrite => self.io_write.clone(),
|
DiskColumn::IoWrite => self.io_write.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(text)
|
Some(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn column_widths<C: DataTableColumn<DiskWidgetColumn>>(
|
fn column_widths<C: DataTableColumn<DiskColumn>>(data: &[Self], _columns: &[C]) -> Vec<u16>
|
||||||
data: &[Self], _columns: &[C],
|
|
||||||
) -> Vec<u16>
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
|
@ -159,63 +199,85 @@ impl DataToCell<DiskWidgetColumn> for DiskWidgetData {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DiskTableWidget {
|
pub struct DiskTableWidget {
|
||||||
pub table: SortDataTable<DiskWidgetData, DiskWidgetColumn>,
|
pub table: SortDataTable<DiskWidgetData, DiskColumn>,
|
||||||
pub force_update_data: bool,
|
pub force_update_data: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SortsRow for DiskWidgetColumn {
|
impl SortsRow for DiskColumn {
|
||||||
type DataType = DiskWidgetData;
|
type DataType = DiskWidgetData;
|
||||||
|
|
||||||
fn sort_data(&self, data: &mut [Self::DataType], descending: bool) {
|
fn sort_data(&self, data: &mut [Self::DataType], descending: bool) {
|
||||||
match self {
|
match self {
|
||||||
DiskWidgetColumn::Disk => {
|
DiskColumn::Disk => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.name, &b.name));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.name, &b.name));
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::Mount => {
|
DiskColumn::Mount => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.mount_point, &b.mount_point));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.mount_point, &b.mount_point));
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::Used => {
|
DiskColumn::Used => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.used_bytes, &b.used_bytes));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.used_bytes, &b.used_bytes));
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::UsedPercent => {
|
DiskColumn::UsedPercent => {
|
||||||
data.sort_by(|a, b| {
|
data.sort_by(|a, b| {
|
||||||
sort_partial_fn(descending)(&a.used_percent(), &b.used_percent())
|
sort_partial_fn(descending)(&a.used_percent(), &b.used_percent())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::Free => {
|
DiskColumn::Free => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.free_bytes, &b.free_bytes));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.free_bytes, &b.free_bytes));
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::FreePercent => {
|
DiskColumn::FreePercent => {
|
||||||
data.sort_by(|a, b| {
|
data.sort_by(|a, b| {
|
||||||
sort_partial_fn(descending)(&a.free_percent(), &b.free_percent())
|
sort_partial_fn(descending)(&a.free_percent(), &b.free_percent())
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::Total => {
|
DiskColumn::Total => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.total_bytes, &b.total_bytes));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.total_bytes, &b.total_bytes));
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::IoRead => {
|
DiskColumn::IoRead => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.io_read, &b.io_read));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.io_read, &b.io_read));
|
||||||
}
|
}
|
||||||
DiskWidgetColumn::IoWrite => {
|
DiskColumn::IoWrite => {
|
||||||
data.sort_by(|a, b| sort_partial_fn(descending)(&a.io_write, &b.io_write));
|
data.sort_by(|a, b| sort_partial_fn(descending)(&a.io_write, &b.io_write));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiskTableWidget {
|
const fn create_column(column_type: &DiskColumn) -> SortColumn<DiskColumn> {
|
||||||
pub fn new(config: &AppConfigFields, palette: &ColourPalette) -> Self {
|
match column_type {
|
||||||
let columns = [
|
DiskColumn::Disk => SortColumn::soft(DiskColumn::Disk, Some(0.2)),
|
||||||
SortColumn::soft(DiskWidgetColumn::Disk, Some(0.2)),
|
DiskColumn::Mount => SortColumn::soft(DiskColumn::Mount, Some(0.2)),
|
||||||
SortColumn::soft(DiskWidgetColumn::Mount, Some(0.2)),
|
DiskColumn::Used => SortColumn::hard(DiskColumn::Used, 8).default_descending(),
|
||||||
SortColumn::hard(DiskWidgetColumn::Used, 8).default_descending(),
|
DiskColumn::Free => SortColumn::hard(DiskColumn::Free, 8).default_descending(),
|
||||||
SortColumn::hard(DiskWidgetColumn::Free, 8).default_descending(),
|
DiskColumn::Total => SortColumn::hard(DiskColumn::Total, 9).default_descending(),
|
||||||
SortColumn::hard(DiskWidgetColumn::Total, 9).default_descending(),
|
DiskColumn::UsedPercent => {
|
||||||
SortColumn::hard(DiskWidgetColumn::UsedPercent, 9).default_descending(),
|
SortColumn::hard(DiskColumn::UsedPercent, 9).default_descending()
|
||||||
SortColumn::hard(DiskWidgetColumn::IoRead, 10).default_descending(),
|
}
|
||||||
SortColumn::hard(DiskWidgetColumn::IoWrite, 11).default_descending(),
|
DiskColumn::FreePercent => {
|
||||||
];
|
SortColumn::hard(DiskColumn::FreePercent, 9).default_descending()
|
||||||
|
}
|
||||||
|
DiskColumn::IoRead => SortColumn::hard(DiskColumn::IoRead, 10).default_descending(),
|
||||||
|
DiskColumn::IoWrite => SortColumn::hard(DiskColumn::IoWrite, 11).default_descending(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn default_disk_columns() -> [SortColumn<DiskColumn>; 8] {
|
||||||
|
[
|
||||||
|
create_column(&DiskColumn::Disk),
|
||||||
|
create_column(&DiskColumn::Mount),
|
||||||
|
create_column(&DiskColumn::Used),
|
||||||
|
create_column(&DiskColumn::Free),
|
||||||
|
create_column(&DiskColumn::Total),
|
||||||
|
create_column(&DiskColumn::UsedPercent),
|
||||||
|
create_column(&DiskColumn::IoRead),
|
||||||
|
create_column(&DiskColumn::IoWrite),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskTableWidget {
|
||||||
|
pub fn new(
|
||||||
|
config: &AppConfigFields, palette: &ColourPalette, columns: Option<&[DiskColumn]>,
|
||||||
|
) -> Self {
|
||||||
let props = SortDataTableProps {
|
let props = SortDataTableProps {
|
||||||
inner: DataTableProps {
|
inner: DataTableProps {
|
||||||
title: Some(" Disks ".into()),
|
title: Some(" Disks ".into()),
|
||||||
|
@ -231,9 +293,18 @@ impl DiskTableWidget {
|
||||||
|
|
||||||
let styling = DataTableStyling::from_palette(palette);
|
let styling = DataTableStyling::from_palette(palette);
|
||||||
|
|
||||||
Self {
|
match columns {
|
||||||
table: SortDataTable::new_sortable(columns, props, styling),
|
Some(columns) => {
|
||||||
force_update_data: false,
|
let columns = columns.iter().map(create_column).collect::<Vec<_>>();
|
||||||
|
Self {
|
||||||
|
table: SortDataTable::new_sortable(columns, props, styling),
|
||||||
|
force_update_data: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => Self {
|
||||||
|
table: SortDataTable::new_sortable(default_disk_columns(), props, styling),
|
||||||
|
force_update_data: false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -94,28 +94,14 @@ impl ColumnHeader for ProcColumn {
|
||||||
|
|
||||||
fn header(&self) -> Cow<'static, str> {
|
fn header(&self) -> Cow<'static, str> {
|
||||||
match self {
|
match self {
|
||||||
ProcColumn::CpuPercent => "CPU%(c)",
|
ProcColumn::CpuPercent => "CPU%(c)".into(),
|
||||||
ProcColumn::MemValue => "Mem(m)",
|
ProcColumn::MemValue => "Mem(m)".into(),
|
||||||
ProcColumn::MemPercent => "Mem%(m)",
|
ProcColumn::MemPercent => "Mem%(m)".into(),
|
||||||
ProcColumn::Pid => "PID(p)",
|
ProcColumn::Pid => "PID(p)".into(),
|
||||||
ProcColumn::Count => "Count",
|
ProcColumn::Name => "Name(n)".into(),
|
||||||
ProcColumn::Name => "Name(n)",
|
ProcColumn::Command => "Command(n)".into(),
|
||||||
ProcColumn::Command => "Command(n)",
|
_ => self.text(),
|
||||||
ProcColumn::ReadPerSecond => "R/s",
|
|
||||||
ProcColumn::WritePerSecond => "W/s",
|
|
||||||
ProcColumn::TotalRead => "T.Read",
|
|
||||||
ProcColumn::TotalWrite => "T.Write",
|
|
||||||
ProcColumn::State => "State",
|
|
||||||
ProcColumn::User => "User",
|
|
||||||
ProcColumn::Time => "Time",
|
|
||||||
#[cfg(feature = "gpu")]
|
|
||||||
ProcColumn::GpuMemValue => "GMem",
|
|
||||||
#[cfg(feature = "gpu")]
|
|
||||||
ProcColumn::GpuMemPercent => "GMem%",
|
|
||||||
#[cfg(feature = "gpu")]
|
|
||||||
ProcColumn::GpuUtilPercent => "GPU%",
|
|
||||||
}
|
}
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +200,9 @@ impl<'de> Deserialize<'de> for ProcColumn {
|
||||||
"gmem" | "gmem%" => Ok(ProcColumn::GpuMemPercent),
|
"gmem" | "gmem%" => Ok(ProcColumn::GpuMemPercent),
|
||||||
#[cfg(feature = "gpu")]
|
#[cfg(feature = "gpu")]
|
||||||
"gpu%" => Ok(ProcColumn::GpuUtilPercent),
|
"gpu%" => Ok(ProcColumn::GpuUtilPercent),
|
||||||
_ => Err(serde::de::Error::custom("doesn't match any column type")),
|
_ => Err(serde::de::Error::custom(
|
||||||
|
"doesn't match any process column name",
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,3 +131,11 @@ fn test_invalid_process_column() {
|
||||||
.failure()
|
.failure()
|
||||||
.stderr(predicate::str::contains("doesn't match"));
|
.stderr(predicate::str::contains("doesn't match"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_invalid_disk_column() {
|
||||||
|
btm_command(&["-C", "./tests/invalid_configs/invalid_disk_column.toml"])
|
||||||
|
.assert()
|
||||||
|
.failure()
|
||||||
|
.stderr(predicate::str::contains("doesn't match"));
|
||||||
|
}
|
||||||
|
|
2
tests/invalid_configs/invalid_disk_column.toml
Normal file
2
tests/invalid_configs/invalid_disk_column.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[disk]
|
||||||
|
columns = ["disk", "fake"]
|
|
@ -1,4 +1,5 @@
|
||||||
#:schema none
|
#:schema none
|
||||||
|
# Adding this to avoid a warning from some schema linters
|
||||||
|
|
||||||
[styles]
|
[styles]
|
||||||
theme = "gruvbox"
|
theme = "gruvbox"
|
||||||
|
|
Loading…
Reference in a new issue