diff --git a/CHANGELOG.md b/CHANGELOG.md
index c4b54cff..dc363afe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   - `mem_as_value` is now `process_memory_as_value`.
 - [#1472](https://github.com/ClementTsang/bottom/pull/1472): The following config fields have changed names:
   - `mem_as_value` is now `process_memory_as_value`.
+- [#1481](https://github.com/ClementTsang/bottom/pull/1481): The following config fields have changed names:
+  - `disk_filter` is now `disk.name_filter`.
+  - `mount_filter` is now `disk.mount_filter`.
+  - `temp_filter` is now `temperature.sensor_filter`
+  - `net_filter` is now `network.interface_filter`
 
 ### Bug Fixes
 
diff --git a/Cargo.toml b/Cargo.toml
index 6714ce0a..97eda9f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -143,6 +143,7 @@ clap_complete_nushell = "4.5.1"
 clap_complete_fig = "4.5.0"
 clap_mangen = "0.2.20"
 indoc = "2.0.5"
+# schemars = "0.8.21"
 
 [package.metadata.deb]
 section = "utility"
diff --git a/sample_configs/default_config.toml b/sample_configs/default_config.toml
index 4c8bd8ce..4855b008 100644
--- a/sample_configs/default_config.toml
+++ b/sample_configs/default_config.toml
@@ -80,18 +80,53 @@
 # How much data is stored at once in terms of time.
 #retention = "10m"
 
-# These are flags around the process widget.
-
+# Processes widget configuration
 #[processes]
+# The columns shown by the process widget. The following columns are supported:
+# 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%"]
 
-# [cpu]
+# CPU widget configuration
+#[cpu]
 # One of "all" (default), "average"/"avg"
 # default = "average"
 
+# Disk widget configuration
+#[disk]
+#[disk.name_filter]
+#is_list_ignored = true
+#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
+#regex = true
+#case_sensitive = false
+#whole_word = false
+
+#[disk.mount_filter]
+#is_list_ignored = true
+#list = ["/mnt/.*", "/boot"]
+#regex = true
+#case_sensitive = false
+#whole_word = false
+
+# Temperature widget configuration
+#[temperature]
+#[temperature.sensor_filter]
+#is_list_ignored = true
+#list = ["cpu", "wifi"]
+#regex = false
+#case_sensitive = false
+#whole_word = false
+
+# Network widget configuration
+#[network]
+#[network.interface_filter]
+#is_list_ignored = true
+#list = ["virbr0.*"]
+#regex = true
+#case_sensitive = false
+#whole_word = false
+
 # These are all the components that support custom theming.  Note that colour support
 # will depend on terminal support.
-
 #[colors] # Uncomment if you want to use custom colors
 # Represents the colour of table headers (processes, CPU, disks, temperature).
 #table_header_color="LightBlue"
@@ -160,33 +195,3 @@
 #  [[row.child]]
 #    type="proc"
 #    default=true
-
-# Filters - you can hide specific temperature sensors, network interfaces, and disks using filters.  This is admittedly
-# a bit hard to use as of now, and there is a planned in-app interface for managing this in the future:
-#[disk_filter]
-#is_list_ignored = true
-#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
-#regex = true
-#case_sensitive = false
-#whole_word = false
-
-#[mount_filter]
-#is_list_ignored = true
-#list = ["/mnt/.*", "/boot"]
-#regex = true
-#case_sensitive = false
-#whole_word = false
-
-#[temp_filter]
-#is_list_ignored = true
-#list = ["cpu", "wifi"]
-#regex = false
-#case_sensitive = false
-#whole_word = false
-
-#[net_filter]
-#is_list_ignored = true
-#list = ["virbr0.*"]
-#regex = true
-#case_sensitive = false
-#whole_word = false
diff --git a/src/app.rs b/src/app.rs
index 44976346..ef3fb280 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -421,7 +421,8 @@ impl App {
             pws.is_sort_open = !pws.is_sort_open;
             pws.force_rerender = true;
 
-            // If the sort is now open, move left. Otherwise, if the proc sort was selected, force move right.
+            // If the sort is now open, move left. Otherwise, if the proc sort was selected,
+            // force move right.
             if pws.is_sort_open {
                 pws.sort_table.set_position(pws.table.sort_index());
                 self.move_widget_selection(&WidgetDirection::Left);
@@ -1054,13 +1055,15 @@ impl App {
                 .widget_states
                 .get_mut(&(self.current_widget.widget_id - 1))
             {
-                // Traverse backwards from the current cursor location until you hit non-whitespace characters,
-                // then continue to traverse (and delete) backwards until you hit a whitespace character.  Halt.
+                // Traverse backwards from the current cursor location until you hit
+                // non-whitespace characters, then continue to traverse (and
+                // delete) backwards until you hit a whitespace character.  Halt.
 
                 // So... first, let's get our current cursor position in terms of char indices.
                 let end_index = proc_widget_state.cursor_char_index();
 
-                // Then, let's crawl backwards until we hit our location, and store the "head"...
+                // Then, let's crawl backwards until we hit our location, and store the
+                // "head"...
                 let query = proc_widget_state.current_search_query();
                 let mut start_index = 0;
                 let mut saw_non_whitespace = false;
@@ -1617,7 +1620,8 @@ impl App {
                                     if let Some(basic_table_widget_state) =
                                         &mut self.states.basic_table_widget_state
                                     {
-                                        // We also want to move towards Proc if we had set it to ProcSort.
+                                        // We also want to move towards Proc if we had set it to
+                                        // ProcSort.
                                         if let BottomWidgetType::ProcSort =
                                             basic_table_widget_state.currently_displayed_widget_type
                                         {
@@ -2505,20 +2509,22 @@ impl App {
         }
     }
 
-    /// Moves the mouse to the widget that was clicked on, then propagates the click down to be
-    /// handled by the widget specifically.
+    /// Moves the mouse to the widget that was clicked on, then propagates the
+    /// click down to be handled by the widget specifically.
     pub fn on_left_mouse_up(&mut self, x: u16, y: u16) {
-        // Pretty dead simple - iterate through the widget map and go to the widget where the click
-        // is within.
+        // Pretty dead simple - iterate through the widget map and go to the widget
+        // where the click is within.
 
         // TODO: [REFACTOR] might want to refactor this, it's really ugly.
-        // TODO: [REFACTOR] Might wanna refactor ALL state things in general, currently everything
-        // is grouped up as an app state.  We should separate stuff like event state and gui state and etc.
+        // TODO: [REFACTOR] Might wanna refactor ALL state things in general, currently
+        // everything is grouped up as an app state.  We should separate stuff
+        // like event state and gui state and etc.
 
-        // TODO: [MOUSE] double click functionality...?  We would do this above all other actions and SC if needed.
+        // TODO: [MOUSE] double click functionality...?  We would do this above all
+        // other actions and SC if needed.
 
-        // Short circuit if we're in basic table... we might have to handle the basic table arrow
-        // case here...
+        // Short circuit if we're in basic table... we might have to handle the basic
+        // table arrow case here...
 
         if let Some(bt) = &mut self.states.basic_table_widget_state {
             if let (
@@ -2582,8 +2588,8 @@ impl App {
             }
         }
 
-        // Second short circuit --- are we in the dd dialog state?  If so, only check yes/no/signals
-        // and bail after.
+        // Second short circuit --- are we in the dd dialog state?  If so, only check
+        // yes/no/signals and bail after.
         if self.is_in_dialog() {
             match self.delete_dialog_state.button_positions.iter().find(
                 |(tl_x, tl_y, br_x, br_y, _idx)| {
@@ -2649,7 +2655,8 @@ impl App {
         ) {
             let border_offset = u16::from(self.is_drawing_border());
 
-            // This check ensures the click isn't actually just clicking on the bottom border.
+            // This check ensures the click isn't actually just clicking on the bottom
+            // border.
             if y < (brc_y - border_offset) {
                 match &self.current_widget.widget_type {
                     BottomWidgetType::Proc
@@ -2682,8 +2689,10 @@ impl App {
 
                                             self.change_process_position(change);
 
-                                            // If in tree mode, also check to see if this click is on
-                                            // the same entry as the already selected one - if it is,
+                                            // If in tree mode, also check to see if this click is
+                                            // on
+                                            // the same entry as the already selected one - if it
+                                            // is,
                                             // then we minimize.
                                             if is_tree_mode && change == 0 {
                                                 self.toggle_collapsing_process_branch();
@@ -2755,8 +2764,9 @@ impl App {
                                 _ => {}
                             }
                         } else {
-                            // We might have clicked on a header!  Check if we only exceeded the table + border offset, and
-                            // it's implied we exceeded the gap offset.
+                            // We might have clicked on a header!  Check if we only exceeded the
+                            // table + border offset, and it's implied
+                            // we exceeded the gap offset.
                             if clicked_entry == border_offset {
                                 match &self.current_widget.widget_type {
                                     BottomWidgetType::Proc => {
@@ -2851,8 +2861,9 @@ impl App {
 
     /// A quick and dirty way to handle paste events.
     pub fn handle_paste(&mut self, paste: String) {
-        // Partially copy-pasted from the single-char variant; should probably clean up this process in the future.
-        // In particular, encapsulate this entire logic and add some tests to make it less potentially error-prone.
+        // Partially copy-pasted from the single-char variant; should probably clean up
+        // this process in the future. In particular, encapsulate this entire
+        // logic and add some tests to make it less potentially error-prone.
         let is_in_search_widget = self.is_in_search_widget();
         if let Some(proc_widget_state) = self
             .states
diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs
index 104778d4..f756862a 100644
--- a/src/app/data_farmer.rs
+++ b/src/app/data_farmer.rs
@@ -2,10 +2,10 @@
 //! a better name for the file.  Since I called data collection "harvesting",
 //! then this is the farmer I guess.
 //!
-//! Essentially the main goal is to shift the initial calculation and distribution
-//! of joiner points and data to one central location that will only do it
-//! *once* upon receiving the data --- as opposed to doing it on canvas draw,
-//! which will be a costly process.
+//! Essentially the main goal is to shift the initial calculation and
+//! distribution of joiner points and data to one central location that will
+//! only do it *once* upon receiving the data --- as opposed to doing it on
+//! canvas draw, which will be a costly process.
 //!
 //! This will also handle the *cleaning* of stale data.  That should be done
 //! in some manner (timer on another thread, some loop) that will occasionally
@@ -102,8 +102,8 @@ impl ProcessData {
 /// collected, and what is needed to convert into a displayable form.
 ///
 /// If the app is *frozen* - that is, we do not want to *display* any changing
-/// data, keep updating this. As of 2021-09-08, we just clone the current collection
-/// when it freezes to have a snapshot floating around.
+/// data, keep updating this. As of 2021-09-08, we just clone the current
+/// collection when it freezes to have a snapshot floating around.
 ///
 /// Note that with this method, the *app* thread is responsible for cleaning -
 /// not the data collector.
@@ -355,7 +355,8 @@ impl DataCollection {
                     #[cfg(feature = "zfs")]
                     {
                         if !device.name.starts_with('/') {
-                            Some(device.name.as_str()) // use the whole zfs dataset name
+                            Some(device.name.as_str()) // use the whole zfs
+                                                       // dataset name
                         } else {
                             device.name.split('/').last()
                         }
diff --git a/src/app/filter.rs b/src/app/filter.rs
index 574fdab4..c313c0b6 100644
--- a/src/app/filter.rs
+++ b/src/app/filter.rs
@@ -16,8 +16,9 @@ impl Filter {
     #[inline]
     pub(crate) fn keep_entry(&self, value: &str) -> bool {
         if self.has_match(value) {
-            // If a match is found, then if we wanted to ignore if we match, return false. If we want
-            // to keep if we match, return true. Thus, return the inverse of `is_list_ignored`.
+            // If a match is found, then if we wanted to ignore if we match, return false.
+            // If we want to keep if we match, return true. Thus, return the
+            // inverse of `is_list_ignored`.
             !self.is_list_ignored
         } else {
             self.is_list_ignored
diff --git a/src/app/frozen_state.rs b/src/app/frozen_state.rs
index 6be62651..fc828311 100644
--- a/src/app/frozen_state.rs
+++ b/src/app/frozen_state.rs
@@ -1,7 +1,8 @@
 use super::DataCollection;
 
-/// The [`FrozenState`] indicates whether the application state should be frozen. It is either not frozen or
-/// frozen and containing a copy of the state at the time.
+/// The [`FrozenState`] indicates whether the application state should be
+/// frozen. It is either not frozen or frozen and containing a copy of the state
+/// at the time.
 pub enum FrozenState {
     NotFrozen,
     Frozen(Box<DataCollection>),
diff --git a/src/app/process_killer.rs b/src/app/process_killer.rs
index b3b198ee..3c2aaef7 100644
--- a/src/app/process_killer.rs
+++ b/src/app/process_killer.rs
@@ -1,4 +1,5 @@
-//! This file is meant to house (OS specific) implementations on how to kill processes.
+//! This file is meant to house (OS specific) implementations on how to kill
+//! processes.
 
 #[cfg(target_os = "windows")]
 use windows::Win32::{
@@ -61,7 +62,8 @@ pub fn kill_process_given_pid(pid: Pid) -> crate::utils::error::Result<()> {
 /// Kills a process, given a PID, for UNIX.
 #[cfg(target_family = "unix")]
 pub fn kill_process_given_pid(pid: Pid, signal: usize) -> crate::utils::error::Result<()> {
-    // SAFETY: the signal should be valid, and we act properly on an error (exit code not 0).
+    // SAFETY: the signal should be valid, and we act properly on an error (exit
+    // code not 0).
 
     let output = unsafe { libc::kill(pid, signal as i32) };
     if output != 0 {
diff --git a/src/app/query.rs b/src/app/query.rs
index 5737cbc7..c642000a 100644
--- a/src/app/query.rs
+++ b/src/app/query.rs
@@ -26,13 +26,15 @@ const OR_LIST: [&str; 2] = ["or", "||"];
 const AND_LIST: [&str; 2] = ["and", "&&"];
 
 /// In charge of parsing the given query.
-/// We are defining the following language for a query (case-insensitive prefixes):
+/// We are defining the following language for a query (case-insensitive
+/// prefixes):
 ///
 /// - Process names: No prefix required, can use regex, match word, or case.
-///   Enclosing anything, including prefixes, in quotes, means we treat it as an entire process
-///   rather than a prefix.
+///   Enclosing anything, including prefixes, in quotes, means we treat it as an
+///   entire process rather than a prefix.
 /// - PIDs: Use prefix `pid`, can use regex or match word (case is irrelevant).
-/// - CPU: Use prefix `cpu`, cannot use r/m/c (regex, match word, case).  Can compare.
+/// - CPU: Use prefix `cpu`, cannot use r/m/c (regex, match word, case).  Can
+///   compare.
 /// - MEM: Use prefix `mem`, cannot use r/m/c.  Can compare.
 /// - STATE: Use prefix `state`, can use regex, match word, or case.
 /// - USER: Use prefix `user`, can use regex, match word, or case.
@@ -41,9 +43,10 @@ const AND_LIST: [&str; 2] = ["and", "&&"];
 /// - Total read: Use prefix `read`.  Can compare.
 /// - Total write: Use prefix `write`.  Can compare.
 ///
-/// For queries, whitespaces are our delimiters.  We will merge together any adjacent non-prefixed
-/// or quoted elements after splitting to treat as process names.
-/// Furthermore, we want to support boolean joiners like AND and OR, and brackets.
+/// For queries, whitespaces are our delimiters.  We will merge together any
+/// adjacent non-prefixed or quoted elements after splitting to treat as process
+/// names. Furthermore, we want to support boolean joiners like AND and OR, and
+/// brackets.
 pub fn parse_query(
     search_query: &str, is_searching_whole_word: bool, is_ignoring_case: bool,
     is_searching_with_regex: bool,
@@ -176,8 +179,9 @@ pub fn parse_query(
         if let Some(queue_top) = query.pop_front() {
             if inside_quotation {
                 if queue_top == "\"" {
-                    // This means we hit something like "".  Return an empty prefix, and to deal with
-                    // the close quote checker, add one to the top of the stack.  Ugly fix but whatever.
+                    // This means we hit something like "".  Return an empty prefix, and to deal
+                    // with the close quote checker, add one to the top of the
+                    // stack.  Ugly fix but whatever.
                     query.push_front("\"".to_string());
                     return Ok(Prefix {
                         or: None,
@@ -268,8 +272,9 @@ pub fn parse_query(
             } else if queue_top == ")" {
                 return Err(QueryError("Missing opening parentheses".into()));
             } else if queue_top == "\"" {
-                // Similar to parentheses, trap and check for missing closing quotes.  Note, however, that we
-                // will DIRECTLY call another process_prefix call...
+                // Similar to parentheses, trap and check for missing closing quotes.  Note,
+                // however, that we will DIRECTLY call another process_prefix
+                // call...
 
                 let prefix = process_prefix(query, true)?;
                 if let Some(close_paren) = query.pop_front() {
@@ -308,10 +313,12 @@ pub fn parse_query(
                                     // - (test)
                                     // - (test
                                     // - test)
-                                    // These are split into 2 to 3 different strings due to parentheses being
+                                    // These are split into 2 to 3 different strings due to
+                                    // parentheses being
                                     // delimiters in our query system.
                                     //
-                                    // Do we want these to be valid?  They should, as a string, right?
+                                    // Do we want these to be valid?  They should, as a string,
+                                    // right?
 
                                     return Ok(Prefix {
                                         or: None,
@@ -385,8 +392,8 @@ pub fn parse_query(
                             let mut condition: Option<QueryComparison> = None;
                             let mut value: Option<f64> = None;
 
-                            // TODO: Jeez, what the heck did I write here... add some tests and clean this up in the
-                            // future.
+                            // TODO: Jeez, what the heck did I write here... add some tests and
+                            // clean this up in the future.
                             if content == "=" {
                                 condition = Some(QueryComparison::Equal);
                                 if let Some(queue_next) = query.pop_front() {
@@ -423,8 +430,9 @@ pub fn parse_query(
 
                             if let Some(condition) = condition {
                                 if let Some(read_value) = value {
-                                    // Note that the values *might* have a unit or need to be parsed differently
-                                    // based on the prefix type!
+                                    // Note that the values *might* have a unit or need to be parsed
+                                    // differently based on the
+                                    // prefix type!
 
                                     let mut value = read_value;
 
@@ -691,7 +699,8 @@ impl std::str::FromStr for PrefixType {
     }
 }
 
-// TODO: This is also jank and could be better represented. Add tests, then clean up!
+// TODO: This is also jank and could be better represented. Add tests, then
+// clean up!
 #[derive(Default)]
 pub struct Prefix {
     pub or: Option<Box<Or>>,
diff --git a/src/app/states.rs b/src/app/states.rs
index 0088e53d..28ce85c8 100644
--- a/src/app/states.rs
+++ b/src/app/states.rs
@@ -112,7 +112,8 @@ impl Default for AppSearchState {
 }
 
 impl AppSearchState {
-    /// Resets the [`AppSearchState`] to its default state, albeit still enabled.
+    /// Resets the [`AppSearchState`] to its default state, albeit still
+    /// enabled.
     pub fn reset(&mut self) {
         *self = AppSearchState {
             is_enabled: self.is_enabled,
@@ -161,7 +162,8 @@ impl AppSearchState {
                         // Use the current index.
                         start_index
                     } else if cursor_range.end >= available_width {
-                        // If the current position is past the last visible element, skip until we see it.
+                        // If the current position is past the last visible element, skip until we
+                        // see it.
 
                         let mut index = 0;
                         for i in 0..(cursor_index + 1) {
@@ -211,7 +213,8 @@ impl AppSearchState {
             Ok(_) => {}
             Err(err) => match err {
                 GraphemeIncomplete::PreContext(ctx) => {
-                    // Provide the entire string as context. Not efficient but should resolve failures.
+                    // Provide the entire string as context. Not efficient but should resolve
+                    // failures.
                     self.grapheme_cursor
                         .provide_context(&self.current_search_query[0..ctx], 0);
 
@@ -233,7 +236,8 @@ impl AppSearchState {
             Ok(_) => {}
             Err(err) => match err {
                 GraphemeIncomplete::PreContext(ctx) => {
-                    // Provide the entire string as context. Not efficient but should resolve failures.
+                    // Provide the entire string as context. Not efficient but should resolve
+                    // failures.
                     self.grapheme_cursor
                         .provide_context(&self.current_search_query[0..ctx], 0);
 
diff --git a/src/canvas.rs b/src/canvas.rs
index ca144c15..9b587b32 100644
--- a/src/canvas.rs
+++ b/src/canvas.rs
@@ -260,7 +260,8 @@ impl Painter {
                 let middle_dialog_chunk = Layout::default()
                     .direction(Direction::Horizontal)
                     .constraints(if terminal_width < 100 {
-                        // TODO: [REFACTOR] The point we start changing size at currently hard-coded in.
+                        // TODO: [REFACTOR] The point we start changing size at currently hard-coded
+                        // in.
                         [
                             Constraint::Percentage(0),
                             Constraint::Percentage(100),
@@ -386,7 +387,8 @@ impl Painter {
 
                 let actual_cpu_data_len = app_state.converted_data.cpu_data.len().saturating_sub(1);
 
-                // This fixes #397, apparently if the height is 1, it can't render the CPU bars...
+                // This fixes #397, apparently if the height is 1, it can't render the CPU
+                // bars...
                 let cpu_height = {
                     let c =
                         (actual_cpu_data_len / 4) as u16 + u16::from(actual_cpu_data_len % 4 != 0);
@@ -499,15 +501,15 @@ impl Painter {
                 }
 
                 if self.derived_widget_draw_locs.is_empty() || app_state.is_force_redraw {
-                    // TODO: Can I remove this? Does ratatui's layout constraints work properly for fixing
-                    // https://github.com/ClementTsang/bottom/issues/896 now?
+                    // TODO: Can I remove this? Does ratatui's layout constraints work properly for
+                    // fixing https://github.com/ClementTsang/bottom/issues/896 now?
                     fn get_constraints(
                         direction: Direction, constraints: &[LayoutConstraint], area: Rect,
                     ) -> Vec<Rect> {
                         // Order of operations:
                         // - Ratios first + canvas-handled (which is just zero)
-                        // - Then any flex-grows to take up remaining space; divide amongst remaining
-                        //   hand out any remaining space
+                        // - Then any flex-grows to take up remaining space; divide amongst
+                        //   remaining hand out any remaining space
 
                         #[derive(Debug, Default, Clone, Copy)]
                         struct Size {
@@ -688,7 +690,8 @@ impl Painter {
                                     &col_rows.children
                                 )
                                 .map(|(draw_loc, col_row_constraint_vec, widgets)| {
-                                    // Note that col_row_constraint_vec CONTAINS the widget constraints
+                                    // Note that col_row_constraint_vec CONTAINS the widget
+                                    // constraints
                                     let widget_draw_locs = get_constraints(
                                         Direction::Horizontal,
                                         col_row_constraint_vec.as_slice(),
diff --git a/src/canvas/components/data_table.rs b/src/canvas/components/data_table.rs
index f4e26f0c..ba92e077 100644
--- a/src/canvas/components/data_table.rs
+++ b/src/canvas/components/data_table.rs
@@ -20,13 +20,15 @@ use crate::utils::general::ClampExt;
 
 /// A [`DataTable`] is a component that displays data in a tabular form.
 ///
-/// Note that [`DataTable`] takes a generic type `S`, bounded by [`SortType`]. This controls whether this table
-/// expects sorted data or not, with two expected types:
+/// Note that [`DataTable`] takes a generic type `S`, bounded by [`SortType`].
+/// This controls whether this table expects sorted data or not, with two
+/// expected types:
 ///
-/// - [`Unsortable`]: The default if otherwise not specified. This table does not expect sorted data.
-/// - [`Sortable`]: This table expects sorted data, and there are helper functions to
-///   facilitate things like sorting based on a selected column, shortcut column selection support, mouse column
-///   selection support, etc.
+/// - [`Unsortable`]: The default if otherwise not specified. This table does
+///   not expect sorted data.
+/// - [`Sortable`]: This table expects sorted data, and there are helper
+///   functions to facilitate things like sorting based on a selected column,
+///   shortcut column selection support, mouse column selection support, etc.
 pub struct DataTable<DataType, Header, S = Unsortable, C = Column<Header>> {
     pub columns: Vec<C>,
     pub state: DataTableState,
@@ -89,8 +91,9 @@ impl<DataType: DataToCell<H>, H: ColumnHeader, S: SortType, C: DataTableColumn<H
         }
     }
 
-    /// Increments the scroll position if possible by a positive/negative offset. If there is a
-    /// valid change, this function will also return the new position wrapped in an [`Option`].
+    /// Increments the scroll position if possible by a positive/negative
+    /// offset. If there is a valid change, this function will also return
+    /// the new position wrapped in an [`Option`].
     pub fn increment_position(&mut self, change: i64) -> Option<usize> {
         let max_index = self.data.len();
         let current_index = self.state.current_index;
diff --git a/src/canvas/components/data_table/column.rs b/src/canvas/components/data_table/column.rs
index 7bbfcbfa..556fbbbd 100644
--- a/src/canvas/components/data_table/column.rs
+++ b/src/canvas/components/data_table/column.rs
@@ -7,17 +7,20 @@ use std::{
 /// A bound on the width of a column.
 #[derive(Clone, Copy, Debug)]
 pub enum ColumnWidthBounds {
-    /// A width of this type is as long as `desired`, but can otherwise shrink and grow up to a point.
+    /// A width of this type is as long as `desired`, but can otherwise shrink
+    /// and grow up to a point.
     Soft {
-        /// The desired, calculated width. Take this if possible as the base starting width.
+        /// The desired, calculated width. Take this if possible as the base
+        /// starting width.
         desired: u16,
 
-        /// The max width, as a percentage of the total width available. If [`None`],
-        /// then it can grow as desired.
+        /// The max width, as a percentage of the total width available. If
+        /// [`None`], then it can grow as desired.
         max_percentage: Option<f32>,
     },
 
-    /// A width of this type is either as long as specified, or does not appear at all.
+    /// A width of this type is either as long as specified, or does not appear
+    /// at all.
     Hard(u16),
 
     /// A width of this type always resizes to the column header's text width.
@@ -28,7 +31,8 @@ pub trait ColumnHeader {
     /// The "text" version of the column header.
     fn text(&self) -> Cow<'static, str>;
 
-    /// The version displayed when drawing the table. Defaults to [`ColumnHeader::text`].
+    /// The version displayed when drawing the table. Defaults to
+    /// [`ColumnHeader::text`].
     #[inline(always)]
     fn header(&self) -> Cow<'static, str> {
         self.text()
@@ -63,8 +67,9 @@ pub trait DataTableColumn<H: ColumnHeader> {
     /// The actually displayed "header".
     fn header(&self) -> Cow<'static, str>;
 
-    /// The header length, along with any required additional lengths for things like arrows.
-    /// Defaults to getting the length of [`DataTableColumn::header`].
+    /// The header length, along with any required additional lengths for things
+    /// like arrows. Defaults to getting the length of
+    /// [`DataTableColumn::header`].
     fn header_len(&self) -> usize {
         self.header().len()
     }
@@ -78,7 +83,8 @@ pub struct Column<H> {
     /// A restriction on this column's width.
     bounds: ColumnWidthBounds,
 
-    /// Marks that this column is currently "hidden", and should *always* be skipped.
+    /// Marks that this column is currently "hidden", and should *always* be
+    /// skipped.
     is_hidden: bool,
 }
 
@@ -148,10 +154,13 @@ impl<H: ColumnHeader> Column<H> {
 }
 
 pub trait CalculateColumnWidths<H> {
-    /// Calculates widths for the columns of this table, given the current width when called.
+    /// Calculates widths for the columns of this table, given the current width
+    /// when called.
     ///
-    /// * `total_width` is the total width on the canvas that the columns can try and work with.
-    /// * `left_to_right` is whether to size from left-to-right (`true`) or right-to-left (`false`).
+    /// * `total_width` is the total width on the canvas that the columns can
+    ///   try and work with.
+    /// * `left_to_right` is whether to size from left-to-right (`true`) or
+    ///   right-to-left (`false`).
     fn calculate_column_widths(&self, total_width: u16, left_to_right: bool) -> Vec<NonZeroU16>;
 }
 
diff --git a/src/canvas/components/data_table/data_type.rs b/src/canvas/components/data_table/data_type.rs
index ebac3c07..c027d63e 100644
--- a/src/canvas/components/data_table/data_type.rs
+++ b/src/canvas/components/data_table/data_type.rs
@@ -9,8 +9,9 @@ pub trait DataToCell<H>
 where
     H: ColumnHeader,
 {
-    /// Given data, a column, and its corresponding width, return the string in the cell that will
-    /// be displayed in the [`DataTable`](super::DataTable).
+    /// Given data, a column, and its corresponding width, return the string in
+    /// the cell that will be displayed in the
+    /// [`DataTable`](super::DataTable).
     fn to_cell(&self, column: &H, calculated_width: NonZeroU16) -> Option<Cow<'static, str>>;
 
     /// Apply styling to the generated [`Row`] of cells.
diff --git a/src/canvas/components/data_table/draw.rs b/src/canvas/components/data_table/draw.rs
index 46304f83..0fe5bb91 100644
--- a/src/canvas/components/data_table/draw.rs
+++ b/src/canvas/components/data_table/draw.rs
@@ -202,7 +202,8 @@ where
 
             if !self.data.is_empty() || !self.first_draw {
                 if self.first_draw {
-                    self.first_draw = false; // TODO: Doing it this way is fine, but it could be done better (e.g. showing custom no results/entries message)
+                    self.first_draw = false; // TODO: Doing it this way is fine, but it could be done better (e.g. showing
+                                             // custom no results/entries message)
                     if let Some(first_index) = self.first_index {
                         self.set_position(first_index);
                     }
diff --git a/src/canvas/components/data_table/sortable.rs b/src/canvas/components/data_table/sortable.rs
index 1ece31cc..659fc939 100644
--- a/src/canvas/components/data_table/sortable.rs
+++ b/src/canvas/components/data_table/sortable.rs
@@ -46,7 +46,8 @@ pub struct Sortable {
 }
 
 /// The [`SortType`] trait is meant to be used in the typing of a [`DataTable`]
-/// to denote whether the table is meant to display/store sorted or unsorted data.
+/// to denote whether the table is meant to display/store sorted or unsorted
+/// data.
 ///
 /// Note that the trait is [sealed](https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed),
 /// and therefore only [`Unsortable`] and [`Sortable`] can implement it.
@@ -97,9 +98,10 @@ impl SortType for Sortable {
                             SortOrder::Ascending => UP_ARROW,
                             SortOrder::Descending => DOWN_ARROW,
                         };
-                        // TODO: I think I can get away with removing the truncate_to_text call since
-                        // I almost always bind to at least the header size...
-                        // TODO: Or should we instead truncate but ALWAYS leave the arrow at the end?
+                        // TODO: I think I can get away with removing the truncate_to_text call
+                        // since I almost always bind to at least the header
+                        // size... TODO: Or should we instead truncate but
+                        // ALWAYS leave the arrow at the end?
                         truncate_to_text(&concat_string!(c.header(), arrow), width.get())
                     } else {
                         truncate_to_text(&c.header(), width.get())
@@ -127,7 +129,8 @@ pub struct SortColumn<T> {
     /// A restriction on this column's width.
     pub bounds: ColumnWidthBounds,
 
-    /// Marks that this column is currently "hidden", and should *always* be skipped.
+    /// Marks that this column is currently "hidden", and should *always* be
+    /// skipped.
     pub is_hidden: bool,
 }
 
@@ -178,8 +181,9 @@ impl<D, T> SortColumn<T>
 where
     T: ColumnHeader + SortsRow<DataType = D>,
 {
-    /// Creates a new [`SortColumn`] with a width that follows the header width, which has no shortcut and sorts by
-    /// default in ascending order ([`SortOrder::Ascending`]).
+    /// Creates a new [`SortColumn`] with a width that follows the header width,
+    /// which has no shortcut and sorts by default in ascending order
+    /// ([`SortOrder::Ascending`]).
     pub fn new(inner: T) -> Self {
         Self {
             inner,
@@ -189,8 +193,8 @@ where
         }
     }
 
-    /// Creates a new [`SortColumn`] with a hard width, which has no shortcut and sorts by default in
-    /// ascending order ([`SortOrder::Ascending`]).
+    /// Creates a new [`SortColumn`] with a hard width, which has no shortcut
+    /// and sorts by default in ascending order ([`SortOrder::Ascending`]).
     pub fn hard(inner: T, width: u16) -> Self {
         Self {
             inner,
@@ -200,8 +204,8 @@ where
         }
     }
 
-    /// Creates a new [`SortColumn`] with a soft width, which has no shortcut and sorts by default in
-    /// ascending order ([`SortOrder::Ascending`]).
+    /// Creates a new [`SortColumn`] with a soft width, which has no shortcut
+    /// and sorts by default in ascending order ([`SortOrder::Ascending`]).
     pub fn soft(inner: T, max_percentage: Option<f32>) -> Self {
         Self {
             inner,
@@ -226,7 +230,8 @@ where
         self
     }
 
-    /// Given a [`SortColumn`] and the sort order, sort a mutable slice of associated data.
+    /// Given a [`SortColumn`] and the sort order, sort a mutable slice of
+    /// associated data.
     pub fn sort_by(&self, data: &mut [D], order: SortOrder) {
         let descending = matches!(order, SortOrder::Descending);
         self.inner.sort_data(data, descending);
@@ -284,11 +289,11 @@ where
         }
     }
 
-    /// Given some `x` and `y`, if possible, select the corresponding column or toggle the column if already selected,
-    /// and otherwise do nothing.
+    /// Given some `x` and `y`, if possible, select the corresponding column or
+    /// toggle the column if already selected, and otherwise do nothing.
     ///
-    /// If there was some update, the corresponding column type will be returned. If nothing happens, [`None`] is
-    /// returned.
+    /// If there was some update, the corresponding column type will be
+    /// returned. If nothing happens, [`None`] is returned.
     pub fn try_select_location(&mut self, x: u16, y: u16) -> Option<usize> {
         if self.state.inner_rect.height > 1 && self.state.inner_rect.y == y {
             if let Some(index) = self.get_range(x) {
@@ -304,10 +309,11 @@ where
 
     /// Updates the sort index, and sets the sort order as appropriate.
     ///
-    /// If the index is different from the previous one, it will move to the new index and set the sort order
-    /// to the prescribed default sort order.
+    /// If the index is different from the previous one, it will move to the new
+    /// index and set the sort order to the prescribed default sort order.
     ///
-    /// If the index is the same as the previous one, it will simply toggle the current sort order.
+    /// If the index is the same as the previous one, it will simply toggle the
+    /// current sort order.
     pub fn set_sort_index(&mut self, index: usize) {
         if self.sort_type.sort_index == index {
             self.toggle_order();
diff --git a/src/canvas/components/time_graph.rs b/src/canvas/components/time_graph.rs
index 361b502e..fa069760 100644
--- a/src/canvas/components/time_graph.rs
+++ b/src/canvas/components/time_graph.rs
@@ -23,7 +23,8 @@ pub struct GraphData<'a> {
 }
 
 pub struct TimeGraph<'a> {
-    /// The min and max x boundaries. Expects a f64 representing the time range in milliseconds.
+    /// The min and max x boundaries. Expects a f64 representing the time range
+    /// in milliseconds.
     pub x_bounds: [u64; 2],
 
     /// Whether to hide the time/x-labels.
@@ -99,7 +100,8 @@ impl<'a> TimeGraph<'a> {
             )
     }
 
-    /// Generates a title for the [`TimeGraph`] widget, given the available space.
+    /// Generates a title for the [`TimeGraph`] widget, given the available
+    /// space.
     fn generate_title(&self, draw_loc: Rect) -> Line<'_> {
         if self.is_expanded {
             let title_base = concat_string!(self.title, "── Esc to go back ");
@@ -121,13 +123,15 @@ impl<'a> TimeGraph<'a> {
         }
     }
 
-    /// Draws a time graph at [`Rect`] location provided by `draw_loc`. A time graph is used to display data points
-    /// throughout time in the x-axis.
+    /// Draws a time graph at [`Rect`] location provided by `draw_loc`. A time
+    /// graph is used to display data points throughout time in the x-axis.
     ///
     /// This time graph:
     /// - Draws with the higher time value on the left, and lower on the right.
-    /// - Expects a [`TimeGraph`] to be passed in, which details how to draw the graph.
-    /// - Expects `graph_data`, which represents *what* data to draw, and various details like style and optional legends.
+    /// - Expects a [`TimeGraph`] to be passed in, which details how to draw the
+    ///   graph.
+    /// - Expects `graph_data`, which represents *what* data to draw, and
+    ///   various details like style and optional legends.
     pub fn draw_time_graph(&self, f: &mut Frame<'_>, draw_loc: Rect, graph_data: &[GraphData<'_>]) {
         let x_axis = self.generate_x_axis();
         let y_axis = self.generate_y_axis();
diff --git a/src/canvas/components/tui_widget/pipe_gauge.rs b/src/canvas/components/tui_widget/pipe_gauge.rs
index 08aa4ca2..f8905e9f 100644
--- a/src/canvas/components/tui_widget/pipe_gauge.rs
+++ b/src/canvas/components/tui_widget/pipe_gauge.rs
@@ -47,8 +47,8 @@ impl<'a> Default for PipeGauge<'a> {
 }
 
 impl<'a> PipeGauge<'a> {
-    /// The ratio, a value from 0.0 to 1.0 (any other greater or less will be clamped)
-    /// represents the portion of the pipe gauge to fill.
+    /// The ratio, a value from 0.0 to 1.0 (any other greater or less will be
+    /// clamped) represents the portion of the pipe gauge to fill.
     ///
     /// Note: passing in NaN will potentially cause problems.
     pub fn ratio(mut self, ratio: f64) -> Self {
@@ -87,7 +87,8 @@ impl<'a> PipeGauge<'a> {
         self
     }
 
-    /// Whether to hide parts of the gauge/label if the inner label wouldn't fit.
+    /// Whether to hide parts of the gauge/label if the inner label wouldn't
+    /// fit.
     pub fn hide_parts(mut self, hide_parts: LabelLimit) -> Self {
         self.hide_parts = hide_parts;
         self
diff --git a/src/canvas/components/tui_widget/time_chart.rs b/src/canvas/components/tui_widget/time_chart.rs
index 05ec4374..922908d2 100644
--- a/src/canvas/components/tui_widget/time_chart.rs
+++ b/src/canvas/components/tui_widget/time_chart.rs
@@ -1,5 +1,5 @@
-//! A [`tui::widgets::Chart`] but slightly more specialized to show right-aligned timeseries
-//! data.
+//! A [`tui::widgets::Chart`] but slightly more specialized to show
+//! right-aligned timeseries data.
 //!
 //! Generally should be updated to be in sync with [`chart.rs`](https://github.com/ratatui-org/ratatui/blob/main/src/widgets/chart.rs);
 //! the specializations are factored out to `time_chart/points.rs`.
@@ -31,7 +31,8 @@ pub type Point = (f64, f64);
 pub struct Axis<'a> {
     /// Title displayed next to axis end
     pub(crate) title: Option<Line<'a>>,
-    /// Bounds for the axis (all data points outside these limits will not be represented)
+    /// Bounds for the axis (all data points outside these limits will not be
+    /// represented)
     pub(crate) bounds: [f64; 2],
     /// A list of labels to put to the left or below the axis
     pub(crate) labels: Option<Vec<Span<'a>>>,
@@ -44,10 +45,11 @@ pub struct Axis<'a> {
 impl<'a> Axis<'a> {
     /// Sets the axis title
     ///
-    /// It will be displayed at the end of the axis. For an X axis this is the right, for a Y axis,
-    /// this is the top.
+    /// It will be displayed at the end of the axis. For an X axis this is the
+    /// right, for a Y axis, this is the top.
     ///
-    /// This is a fluent setter method which must be chained or used as it consumes self
+    /// This is a fluent setter method which must be chained or used as it
+    /// consumes self
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn title<T>(mut self, title: T) -> Axis<'a>
     where
@@ -61,7 +63,8 @@ impl<'a> Axis<'a> {
     ///
     /// In other words, sets the min and max value on this axis.
     ///
-    /// This is a fluent setter method which must be chained or used as it consumes self
+    /// This is a fluent setter method which must be chained or used as it
+    /// consumes self
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn bounds(mut self, bounds: [f64; 2]) -> Axis<'a> {
         self.bounds = bounds;
@@ -238,11 +241,13 @@ impl FromStr for LegendPosition {
 ///
 /// This is the main element composing a [`TimeChart`].
 ///
-/// A dataset can be [named](Dataset::name). Only named datasets will be rendered in the legend.
+/// A dataset can be [named](Dataset::name). Only named datasets will be
+/// rendered in the legend.
 ///
-/// After that, you can pass it data with [`Dataset::data`]. Data is an array of `f64` tuples
-/// (`(f64, f64)`), the first element being X and the second Y. It's also worth noting that, unlike
-/// the [`Rect`], here the Y axis is bottom to top, as in math.
+/// After that, you can pass it data with [`Dataset::data`]. Data is an array of
+/// `f64` tuples (`(f64, f64)`), the first element being X and the second Y.
+/// It's also worth noting that, unlike the [`Rect`], here the Y axis is bottom
+/// to top, as in math.
 #[derive(Debug, Default, Clone, PartialEq)]
 pub struct Dataset<'a> {
     /// Name of the dataset (used in the legend if shown)
@@ -270,12 +275,12 @@ impl<'a> Dataset<'a> {
 
     /// Sets the data points of this dataset
     ///
-    /// Points will then either be rendered as scrattered points or with lines between them
-    /// depending on [`Dataset::graph_type`].
+    /// Points will then either be rendered as scrattered points or with lines
+    /// between them depending on [`Dataset::graph_type`].
     ///
-    /// Data consist in an array of `f64` tuples (`(f64, f64)`), the first element being X and the
-    /// second Y. It's also worth noting that, unlike the [`Rect`], here the Y axis is bottom to
-    /// top, as in math.
+    /// Data consist in an array of `f64` tuples (`(f64, f64)`), the first
+    /// element being X and the second Y. It's also worth noting that,
+    /// unlike the [`Rect`], here the Y axis is bottom to top, as in math.
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn data(mut self, data: &'a [(f64, f64)]) -> Dataset<'a> {
         self.data = data;
@@ -284,12 +289,15 @@ impl<'a> Dataset<'a> {
 
     /// Sets the kind of character to use to display this dataset
     ///
-    /// You can use dots (`•`), blocks (`█`), bars (`▄`), braille (`⠓`, `⣇`, `⣿`) or half-blocks
-    /// (`█`, `▄`, and `▀`). See [symbols::Marker] for more details.
+    /// You can use dots (`•`), blocks (`█`), bars (`▄`), braille (`⠓`, `⣇`,
+    /// `⣿`) or half-blocks (`█`, `▄`, and `▀`). See [symbols::Marker] for
+    /// more details.
     ///
-    /// Note [`Marker::Braille`] requires a font that supports Unicode Braille Patterns.
+    /// Note [`Marker::Braille`] requires a font that supports Unicode Braille
+    /// Patterns.
     ///
-    /// This is a fluent setter method which must be chained or used as it consumes self
+    /// This is a fluent setter method which must be chained or used as it
+    /// consumes self
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn marker(mut self, marker: symbols::Marker) -> Dataset<'a> {
         self.marker = marker;
@@ -298,9 +306,10 @@ impl<'a> Dataset<'a> {
 
     /// Sets how the dataset should be drawn
     ///
-    /// [`TimeChart`] can draw either a [scatter](GraphType::Scatter) or [line](GraphType::Line) charts.
-    /// A scatter will draw only the points in the dataset while a line will also draw a line
-    /// between them. See [`GraphType`] for more details
+    /// [`TimeChart`] can draw either a [scatter](GraphType::Scatter) or
+    /// [line](GraphType::Line) charts. A scatter will draw only the points
+    /// in the dataset while a line will also draw a line between them. See
+    /// [`GraphType`] for more details
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn graph_type(mut self, graph_type: GraphType) -> Dataset<'a> {
         self.graph_type = graph_type;
@@ -309,11 +318,13 @@ impl<'a> Dataset<'a> {
 
     /// Sets the style of this dataset
     ///
-    /// The given style will be used to draw the legend and the data points. Currently the legend
-    /// will use the entire style whereas the data points will only use the foreground.
+    /// The given style will be used to draw the legend and the data points.
+    /// Currently the legend will use the entire style whereas the data
+    /// points will only use the foreground.
     ///
-    /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
-    /// your own type that implements [`Into<Style>`]).
+    /// `style` accepts any type that is convertible to [`Style`] (e.g.
+    /// [`Style`], [`Color`], or your own type that implements
+    /// [`Into<Style>`]).
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn style<S: Into<Style>>(mut self, style: S) -> Dataset<'a> {
         self.style = style.into();
@@ -321,8 +332,8 @@ impl<'a> Dataset<'a> {
     }
 }
 
-/// A container that holds all the infos about where to display each elements of the chart (axis,
-/// labels, legend, ...).
+/// A container that holds all the infos about where to display each elements of
+/// the chart (axis, labels, legend, ...).
 #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
 struct ChartLayout {
     /// Location of the title of the x axis
@@ -343,8 +354,9 @@ struct ChartLayout {
     graph_area: Rect,
 }
 
-/// A "custom" chart, just a slightly tweaked [`tui::widgets::Chart`] from ratatui, but with greater control over the
-/// legend, and built with the idea of drawing data points relative to a time-based x-axis.
+/// A "custom" chart, just a slightly tweaked [`tui::widgets::Chart`] from
+/// ratatui, but with greater control over the legend, and built with the idea
+/// of drawing data points relative to a time-based x-axis.
 ///
 /// Main changes:
 /// - Styling option for the legend box
@@ -368,8 +380,8 @@ pub struct TimeChart<'a> {
     legend_style: Style,
     /// Constraints used to determine whether the legend should be shown or not
     hidden_legend_constraints: (Constraint, Constraint),
-    /// The position detnermine where the legenth is shown or hide regaurdless of
-    /// `hidden_legend_constraints`
+    /// The position detnermine where the legenth is shown or hide regaurdless
+    /// of `hidden_legend_constraints`
     legend_position: Option<LegendPosition>,
     /// The marker type.
     marker: Marker,
@@ -402,8 +414,9 @@ impl<'a> TimeChart<'a> {
 
     /// Sets the style of the entire chart
     ///
-    /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or
-    /// your own type that implements [`Into<Style>`]).
+    /// `style` accepts any type that is convertible to [`Style`] (e.g.
+    /// [`Style`], [`Color`], or your own type that implements
+    /// [`Into<Style>`]).
     ///
     /// Styles of [`Axis`] and [`Dataset`] will have priority over this style.
     #[must_use = "method moves the value of self and returns the modified value"]
@@ -444,14 +457,16 @@ impl<'a> TimeChart<'a> {
         self
     }
 
-    /// Sets the constraints used to determine whether the legend should be shown or not.
+    /// Sets the constraints used to determine whether the legend should be
+    /// shown or not.
     ///
-    /// The tuple's first constraint is used for the width and the second for the height. If the
-    /// legend takes more space than what is allowed by any constraint, the legend is hidden.
-    /// [`Constraint::Min`] is an exception and will always show the legend.
+    /// The tuple's first constraint is used for the width and the second for
+    /// the height. If the legend takes more space than what is allowed by
+    /// any constraint, the legend is hidden. [`Constraint::Min`] is an
+    /// exception and will always show the legend.
     ///
-    /// If this is not set, the default behavior is to hide the legend if it is greater than 25% of
-    /// the chart, either horizontally or vertically.
+    /// If this is not set, the default behavior is to hide the legend if it is
+    /// greater than 25% of the chart, either horizontally or vertically.
     #[must_use = "method moves the value of self and returns the modified value"]
     pub fn hidden_legend_constraints(
         mut self, constraints: (Constraint, Constraint),
@@ -464,8 +479,9 @@ impl<'a> TimeChart<'a> {
     ///
     /// The default is [`LegendPosition::TopRight`].
     ///
-    /// If [`None`] is given, hide the legend even if [`hidden_legend_constraints`] determines it
-    /// should be shown. In contrast, if `Some(...)` is given, [`hidden_legend_constraints`] might
+    /// If [`None`] is given, hide the legend even if
+    /// [`hidden_legend_constraints`] determines it should be shown. In
+    /// contrast, if `Some(...)` is given, [`hidden_legend_constraints`] might
     /// still decide whether to show the legend or not.
     ///
     /// See [`LegendPosition`] for all available positions.
@@ -477,8 +493,8 @@ impl<'a> TimeChart<'a> {
         self
     }
 
-    /// Compute the internal layout of the chart given the area. If the area is too small some
-    /// elements may be automatically hidden
+    /// Compute the internal layout of the chart given the area. If the area is
+    /// too small some elements may be automatically hidden
     fn layout(&self, area: Rect) -> ChartLayout {
         let mut layout = ChartLayout::default();
         if area.height == 0 || area.width == 0 {
@@ -593,7 +609,8 @@ impl<'a> TimeChart<'a> {
             };
             max_width = max(max_width, width_left_of_y_axis);
         }
-        // labels of y axis and first label of x axis can take at most 1/3rd of the total width
+        // labels of y axis and first label of x axis can take at most 1/3rd of the
+        // total width
         max_width.min(area.width / 3)
     }
 
@@ -703,9 +720,9 @@ impl Widget for TimeChart<'_> {
             return;
         }
 
-        // Sample the style of the entire widget. This sample will be used to reset the style of
-        // the cells that are part of the components put on top of the grah area (i.e legend and
-        // axis names).
+        // Sample the style of the entire widget. This sample will be used to reset the
+        // style of the cells that are part of the components put on top of the
+        // grah area (i.e legend and axis names).
         let original_style = buf.get(area.left(), area.top()).style();
 
         let layout = self.layout(chart_area);
@@ -1020,7 +1037,8 @@ mod tests {
         let layout = widget.layout(buffer.area);
 
         assert!(layout.legend_area.is_some());
-        assert_eq!(layout.legend_area.unwrap().height, 4); // 2 for borders, 2 for rows
+        assert_eq!(layout.legend_area.unwrap().height, 4); // 2 for borders, 2
+                                                           // for rows
     }
 
     #[test]
diff --git a/src/canvas/components/tui_widget/time_chart/canvas.rs b/src/canvas/components/tui_widget/time_chart/canvas.rs
index be723bee..7f9b1c92 100644
--- a/src/canvas/components/tui_widget/time_chart/canvas.rs
+++ b/src/canvas/components/tui_widget/time_chart/canvas.rs
@@ -1,10 +1,12 @@
 //! Vendored from <https://github.com/fdehau/tui-rs/blob/fafad6c96109610825aad89c4bba5253e01101ed/src/widgets/canvas/mod.rs>
 //! and <https://github.com/ratatui-org/ratatui/blob/c8dd87918d44fff6d4c3c78e1fc821a3275db1ae/src/widgets/canvas.rs>.
 //!
-//! The main thing this is pulled in for is overriding how `BrailleGrid`'s draw logic works, as changing it is
-//! needed in order to draw all datasets in only one layer back in [`super::TimeChart::render`]. More specifically,
-//! the current implementation in ratatui `|=`s all the cells together if they overlap, but since we are smashing
-//! all the layers together which may have different colours, we instead just _replace_ whatever was in that cell
+//! The main thing this is pulled in for is overriding how `BrailleGrid`'s draw
+//! logic works, as changing it is needed in order to draw all datasets in only
+//! one layer back in [`super::TimeChart::render`]. More specifically,
+//! the current implementation in ratatui `|=`s all the cells together if they
+//! overlap, but since we are smashing all the layers together which may have
+//! different colours, we instead just _replace_ whatever was in that cell
 //! with the newer colour + character.
 //!
 //! See <https://github.com/ClementTsang/bottom/pull/918> and <https://github.com/ClementTsang/bottom/pull/937> for the
@@ -285,19 +287,21 @@ pub struct Painter<'a, 'b> {
     resolution: (f64, f64),
 }
 
-/// The HalfBlockGrid is a grid made up of cells each containing a half block character.
+/// The HalfBlockGrid is a grid made up of cells each containing a half block
+/// character.
 ///
-/// In terminals, each character is usually twice as tall as it is wide. Unicode has a couple of
-/// vertical half block characters, the upper half block '▀' and lower half block '▄' which take up
-/// half the height of a normal character but the full width. Together with an empty space ' ' and a
-/// full block '█', we can effectively double the resolution of a single cell. In addition, because
-/// each character can have a foreground and background color, we can control the color of the upper
-/// and lower half of each cell. This allows us to draw shapes with a resolution of 1x2 "pixels" per
-/// cell.
+/// In terminals, each character is usually twice as tall as it is wide. Unicode
+/// has a couple of vertical half block characters, the upper half block '▀' and
+/// lower half block '▄' which take up half the height of a normal character but
+/// the full width. Together with an empty space ' ' and a full block '█', we
+/// can effectively double the resolution of a single cell. In addition, because
+/// each character can have a foreground and background color, we can control
+/// the color of the upper and lower half of each cell. This allows us to draw
+/// shapes with a resolution of 1x2 "pixels" per cell.
 ///
-/// This allows for more flexibility than the BrailleGrid which only supports a single
-/// foreground color for each 2x4 dots cell, and the CharGrid which only supports a single
-/// character for each cell.
+/// This allows for more flexibility than the BrailleGrid which only supports a
+/// single foreground color for each 2x4 dots cell, and the CharGrid which only
+/// supports a single character for each cell.
 #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
 struct HalfBlockGrid {
     /// width of the grid in number of terminal columns
@@ -309,8 +313,8 @@ struct HalfBlockGrid {
 }
 
 impl HalfBlockGrid {
-    /// Create a new [`HalfBlockGrid`] with the given width and height measured in terminal columns
-    /// and rows respectively.
+    /// Create a new [`HalfBlockGrid`] with the given width and height measured
+    /// in terminal columns and rows respectively.
     fn new(width: u16, height: u16) -> HalfBlockGrid {
         HalfBlockGrid {
             width,
@@ -334,10 +338,11 @@ impl Grid for HalfBlockGrid {
     }
 
     fn save(&self) -> Layer {
-        // Given that we store the pixels in a grid, and that we want to use 2 pixels arranged
-        // vertically to form a single terminal cell, which can be either empty, upper half block,
-        // lower half block or full block, we need examine the pixels in vertical pairs to decide
-        // what character to print in each cell. So these are the 4 states we use to represent each
+        // Given that we store the pixels in a grid, and that we want to use 2 pixels
+        // arranged vertically to form a single terminal cell, which can be
+        // either empty, upper half block, lower half block or full block, we
+        // need examine the pixels in vertical pairs to decide what character to
+        // print in each cell. So these are the 4 states we use to represent each
         // cell:
         //
         // 1. upper: reset, lower: reset => ' ' fg: reset / bg: reset
@@ -345,20 +350,23 @@ impl Grid for HalfBlockGrid {
         // 3. upper: color, lower: reset => '▀' fg: upper color / bg: reset
         // 4. upper: color, lower: color => '▀' fg: upper color / bg: lower color
         //
-        // Note that because the foreground reset color (i.e. default foreground color) is usually
-        // not the same as the background reset color (i.e. default background color), we need to
-        // swap around the colors for that state (2 reset/color).
+        // Note that because the foreground reset color (i.e. default foreground color)
+        // is usually not the same as the background reset color (i.e. default
+        // background color), we need to swap around the colors for that state
+        // (2 reset/color).
         //
-        // When the upper and lower colors are the same, we could continue to use an upper half
-        // block, but we choose to use a full block instead. This allows us to write unit tests that
-        // treat the cell as a single character instead of two half block characters.
+        // When the upper and lower colors are the same, we could continue to use an
+        // upper half block, but we choose to use a full block instead. This
+        // allows us to write unit tests that treat the cell as a single
+        // character instead of two half block characters.
 
-        // Note we implement this slightly differently to what is done in ratatui's repo,
-        // since their version doesn't seem to compile for me...
-        // TODO: Whenever I add this as a valid marker, make sure this works fine with the overriden
-        // time_chart drawing-layer-thing.
+        // Note we implement this slightly differently to what is done in ratatui's
+        // repo, since their version doesn't seem to compile for me...
+        // TODO: Whenever I add this as a valid marker, make sure this works fine with
+        // the overriden time_chart drawing-layer-thing.
 
-        // Join the upper and lower rows, and emit a tuple vector of strings to print, and their colours.
+        // Join the upper and lower rows, and emit a tuple vector of strings to print,
+        // and their colours.
         let (string, colors) = self
             .pixels
             .iter()
@@ -503,8 +511,8 @@ impl<'a> Context<'a> {
     }
 }
 
-/// The Canvas widget may be used to draw more detailed figures using braille patterns (each
-/// cell can have a braille character in 8 different positions).
+/// The Canvas widget may be used to draw more detailed figures using braille
+/// patterns (each cell can have a braille character in 8 different positions).
 pub struct Canvas<'a, F>
 where
     F: Fn(&mut Context<'_>),
@@ -558,9 +566,10 @@ where
         self
     }
 
-    /// Change the type of points used to draw the shapes. By default the braille patterns are used
-    /// as they provide a more fine grained result but you might want to use the simple dot or
-    /// block instead if the targeted terminal does not support those symbols.
+    /// Change the type of points used to draw the shapes. By default the
+    /// braille patterns are used as they provide a more fine grained result
+    /// but you might want to use the simple dot or block instead if the
+    /// targeted terminal does not support those symbols.
     ///
     /// # Examples
     ///
diff --git a/src/canvas/components/tui_widget/time_chart/points.rs b/src/canvas/components/tui_widget/time_chart/points.rs
index 350f9da8..70283500 100644
--- a/src/canvas/components/tui_widget/time_chart/points.rs
+++ b/src/canvas/components/tui_widget/time_chart/points.rs
@@ -22,9 +22,10 @@ impl TimeChart<'_> {
         // See <https://github.com/ClementTsang/bottom/pull/918> and <https://github.com/ClementTsang/bottom/pull/937>
         // for the original motivation.
         //
-        // We also additionally do some interpolation logic because we may get caught missing some points
-        // when drawing, but we generally want to avoid jarring gaps between the edges when there's
-        // a point that is off screen and so a line isn't drawn (right edge generally won't have this issue
+        // We also additionally do some interpolation logic because we may get caught
+        // missing some points when drawing, but we generally want to avoid
+        // jarring gaps between the edges when there's a point that is off
+        // screen and so a line isn't drawn (right edge generally won't have this issue
         // issue but it can happen in some cases).
 
         for dataset in &self.datasets {
@@ -112,7 +113,8 @@ impl TimeChart<'_> {
     }
 }
 
-/// Returns the start index and potential interpolation index given the start time and the dataset.
+/// Returns the start index and potential interpolation index given the start
+/// time and the dataset.
 fn get_start(dataset: &Dataset<'_>, start_bound: f64) -> (usize, Option<usize>) {
     match dataset
         .data
@@ -123,18 +125,20 @@ fn get_start(dataset: &Dataset<'_>, start_bound: f64) -> (usize, Option<usize>)
     }
 }
 
-/// Returns the end position and potential interpolation index given the end time and the dataset.
+/// Returns the end position and potential interpolation index given the end
+/// time and the dataset.
 fn get_end(dataset: &Dataset<'_>, end_bound: f64) -> (usize, Option<usize>) {
     match dataset
         .data
         .binary_search_by(|(x, _y)| partial_ordering(x, &end_bound))
     {
-        // In the success case, this means we found an index. Add one since we want to include this index and we
-        // expect to use the returned index as part of a (m..n) range.
+        // In the success case, this means we found an index. Add one since we want to include this
+        // index and we expect to use the returned index as part of a (m..n) range.
         Ok(index) => (index.saturating_add(1), None),
-        // In the fail case, this means we did not find an index, and the returned index is where one would *insert*
-        // the location. This index is where one would insert to fit inside the dataset - and since this is an end
-        // bound, index is, in a sense, already "+1" for our range later.
+        // In the fail case, this means we did not find an index, and the returned index is where
+        // one would *insert* the location. This index is where one would insert to fit
+        // inside the dataset - and since this is an end bound, index is, in a sense,
+        // already "+1" for our range later.
         Err(index) => (index, {
             let sum = index.checked_add(1);
             match sum {
@@ -145,7 +149,8 @@ fn get_end(dataset: &Dataset<'_>, end_bound: f64) -> (usize, Option<usize>) {
     }
 }
 
-/// Returns the y-axis value for a given `x`, given two points to draw a line between.
+/// Returns the y-axis value for a given `x`, given two points to draw a line
+/// between.
 fn interpolate_point(older_point: &Point, newer_point: &Point, x: f64) -> f64 {
     let delta_x = newer_point.0 - older_point.0;
     let delta_y = newer_point.1 - older_point.1;
diff --git a/src/canvas/components/widget_carousel.rs b/src/canvas/components/widget_carousel.rs
index 9f7baba7..fd5b7a4c 100644
--- a/src/canvas/components/widget_carousel.rs
+++ b/src/canvas/components/widget_carousel.rs
@@ -129,15 +129,18 @@ impl Painter {
 
             if app_state.should_get_widget_bounds() {
                 // Some explanations for future readers:
-                // - The "height" as of writing of this entire widget is 2.  If it's 1, it occasionally doesn't draw.
+                // - The "height" as of writing of this entire widget is 2.  If it's 1, it
+                //   occasionally doesn't draw.
                 // - As such, the buttons are only on the lower part of this 2-high widget.
-                // - So, we want to only check at one location, the `draw_loc.y + 1`, and that's it.
+                // - So, we want to only check at one location, the `draw_loc.y + 1`, and that's
+                //   it.
                 // - But why is it "+2" then?  Well, it's because I have a REALLY ugly hack
                 //   for mouse button checking, since most button checks are of the form `(draw_loc.y + draw_loc.height)`,
                 //   and the same for the x and width.  Unfortunately, if you check using >= and <=, the outer bound is
                 //   actually too large - so, we assume all of them are one too big and check via < (see
                 //   https://github.com/ClementTsang/bottom/pull/459 for details).
-                // - So in other words, to make it simple, we keep this to a standard and overshoot by one here.
+                // - So in other words, to make it simple, we keep this to a standard and
+                //   overshoot by one here.
                 if let Some(basic_table) = &mut app_state.states.basic_table_widget_state {
                     basic_table.left_tlc =
                         Some((margined_draw_loc[0].x, margined_draw_loc[0].y + 1));
diff --git a/src/canvas/dialogs/dd_dialog.rs b/src/canvas/dialogs/dd_dialog.rs
index 876552bf..ff41df4a 100644
--- a/src/canvas/dialogs/dd_dialog.rs
+++ b/src/canvas/dialogs/dd_dialog.rs
@@ -249,14 +249,15 @@ impl Painter {
                 const SIGNAL: usize = if cfg!(target_os = "windows") { 1 } else { 15 };
 
                 // This is kinda weird, but the gist is:
-                // - We have three sections; we put our mouse bounding box for the "yes" button at the very right edge
-                //   of the left section and 3 characters back.  We then give it a buffer size of 1 on the x-coordinate.
-                // - Same for the "no" button, except it is the right section and we do it from the start of the right
-                //   section.
+                // - We have three sections; we put our mouse bounding box for the "yes" button
+                //   at the very right edge of the left section and 3 characters back.  We then
+                //   give it a buffer size of 1 on the x-coordinate.
+                // - Same for the "no" button, except it is the right section and we do it from
+                //   the start of the right section.
                 //
-                // Lastly, note that mouse detection for the dd buttons assume correct widths.  As such, we correct
-                // them here and check with >= and <= mouse bound checks, as opposed to how we do it elsewhere with
-                // >= and <.  See https://github.com/ClementTsang/bottom/pull/459 for details.
+                // Lastly, note that mouse detection for the dd buttons assume correct widths.
+                // As such, we correct them here and check with >= and <= mouse
+                // bound checks, as opposed to how we do it elsewhere with >= and <.  See https://github.com/ClementTsang/bottom/pull/459 for details.
                 app_state.delete_dialog_state.button_positions = vec![
                     // Yes
                     (
diff --git a/src/canvas/dialogs/help_dialog.rs b/src/canvas/dialogs/help_dialog.rs
index 9dafeb2e..eac3fbc4 100644
--- a/src/canvas/dialogs/help_dialog.rs
+++ b/src/canvas/dialogs/help_dialog.rs
@@ -63,8 +63,8 @@ impl Painter {
             .border_style(self.colours.border_style);
 
         if app_state.should_get_widget_bounds() {
-            // We must also recalculate how many lines are wrapping to properly get scrolling to work on
-            // small terminal sizes... oh joy.
+            // We must also recalculate how many lines are wrapping to properly get
+            // scrolling to work on small terminal sizes... oh joy.
 
             app_state.help_dialog_state.height = block.inner(draw_loc).height;
 
diff --git a/src/canvas/styling.rs b/src/canvas/styling.rs
index 22a88a71..53ff120b 100644
--- a/src/canvas/styling.rs
+++ b/src/canvas/styling.rs
@@ -6,7 +6,7 @@ use tui::style::{Color, Style};
 
 use super::ColourScheme;
 pub use crate::options::ConfigV1;
-use crate::{constants::*, options::colours::ConfigColours, utils::error};
+use crate::{constants::*, options::colours::ColoursConfig, utils::error};
 
 pub struct CanvasStyling {
     pub currently_selected_text_colour: Color,
@@ -154,7 +154,7 @@ impl CanvasStyling {
         Ok(canvas_colours)
     }
 
-    pub fn set_colours_from_palette(&mut self, colours: &ConfigColours) -> anyhow::Result<()> {
+    pub fn set_colours_from_palette(&mut self, colours: &ColoursConfig) -> anyhow::Result<()> {
         // CPU
         try_set_colour!(self.avg_colour_style, colours, avg_cpu_color);
         try_set_colour!(self.all_colour_style, colours, all_cpu_color);
diff --git a/src/canvas/widgets/battery_display.rs b/src/canvas/widgets/battery_display.rs
index c2cc91bc..6fa95ce0 100644
--- a/src/canvas/widgets/battery_display.rs
+++ b/src/canvas/widgets/battery_display.rs
@@ -111,8 +111,9 @@ impl Painter {
                         tab_click_locs
                             .push(((current_x, current_y), (current_x + width, current_y)));
 
-                        // +4 because we want to go one space, then one space past to get to the '|', then 2 more
-                        // to start at the blank space before the tab label.
+                        // +4 because we want to go one space, then one space past to get to the
+                        // '|', then 2 more to start at the blank space
+                        // before the tab label.
                         current_x += width + 4;
                     }
                     battery_widget_state.tab_click_locs = Some(tab_click_locs);
diff --git a/src/canvas/widgets/cpu_basic.rs b/src/canvas/widgets/cpu_basic.rs
index d0fd539a..81ccad78 100644
--- a/src/canvas/widgets/cpu_basic.rs
+++ b/src/canvas/widgets/cpu_basic.rs
@@ -33,8 +33,8 @@ impl Painter {
             // many rows and columns we have in draw_loc (-2 on both sides for border?).
             // I think what we can do is try to fit in as many in one column as possible.
             // If not, then add a new column.
-            // Then, from this, split the row space across ALL columns.  From there, generate
-            // the desired lengths.
+            // Then, from this, split the row space across ALL columns.  From there,
+            // generate the desired lengths.
 
             if app_state.current_widget.widget_id == widget_id {
                 f.render_widget(
diff --git a/src/canvas/widgets/cpu_graph.rs b/src/canvas/widgets/cpu_graph.rs
index 85200e98..7d7377f7 100644
--- a/src/canvas/widgets/cpu_graph.rs
+++ b/src/canvas/widgets/cpu_graph.rs
@@ -251,7 +251,8 @@ impl Painter {
             .widget_states
             .get_mut(&(widget_id - 1))
         {
-            // TODO: This line (and the one above, see caller) is pretty dumb but I guess needed for now. Refactor if possible!
+            // TODO: This line (and the one above, see caller) is pretty dumb but I guess
+            // needed for now. Refactor if possible!
             cpu_widget_state.is_legend_hidden = false;
 
             let is_on_widget = widget_id == app_state.current_widget.widget_id;
diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs
index ec7e5fbd..227c18ba 100644
--- a/src/canvas/widgets/network_graph.rs
+++ b/src/canvas/widgets/network_graph.rs
@@ -42,8 +42,8 @@ impl Painter {
 
         if app_state.should_get_widget_bounds() {
             // Update draw loc in widget map
-            // Note that in both cases, we always go to the same widget id so it's fine to do it like
-            // this lol.
+            // Note that in both cases, we always go to the same widget id so it's fine to
+            // do it like this lol.
             if let Some(network_widget) = app_state.widget_map.get_mut(&widget_id) {
                 network_widget.top_left_corner = Some((draw_loc.x, draw_loc.y));
                 network_widget.bottom_right_corner =
@@ -74,7 +74,8 @@ impl Painter {
             // TODO: Cache network results: Only update if:
             // - Force update (includes time interval change)
             // - Old max time is off screen
-            // - A new time interval is better and does not fit (check from end of vector to last checked; we only want to update if it is TOO big!)
+            // - A new time interval is better and does not fit (check from end of vector to
+            //   last checked; we only want to update if it is TOO big!)
 
             // Find the maximal rx/tx so we know how to scale, and return it.
             let (_best_time, max_entry) = get_max_entry(
@@ -216,7 +217,8 @@ fn get_max_entry(
     rx: &[Point], tx: &[Point], time_start: f64, network_scale_type: &AxisScaling,
     network_use_binary_prefix: bool,
 ) -> Point {
-    /// Determines a "fake" max value in circumstances where we couldn't find one from the data.
+    /// Determines a "fake" max value in circumstances where we couldn't find
+    /// one from the data.
     fn calculate_missing_max(
         network_scale_type: &AxisScaling, network_use_binary_prefix: bool,
     ) -> f64 {
@@ -238,8 +240,9 @@ fn get_max_entry(
         }
     }
 
-    // First, let's shorten our ranges to actually look.  We can abuse the fact that our rx and tx arrays
-    // are sorted, so we can short-circuit our search to filter out only the relevant data points...
+    // First, let's shorten our ranges to actually look.  We can abuse the fact that
+    // our rx and tx arrays are sorted, so we can short-circuit our search to
+    // filter out only the relevant data points...
     let filtered_rx = if let (Some(rx_start), Some(rx_end)) = (
         rx.iter().position(|(time, _data)| *time >= time_start),
         rx.iter().rposition(|(time, _data)| *time <= 0.0),
@@ -337,26 +340,31 @@ fn adjust_network_data_point(
     network_use_binary_prefix: bool,
 ) -> (f64, Vec<String>) {
     // So, we're going with an approach like this for linear data:
-    // - Main goal is to maximize the amount of information displayed given a specific height.
-    //   We don't want to drown out some data if the ranges are too far though!  Nor do we want to filter
-    //   out too much data...
-    // - Change the y-axis unit (kilo/kibi, mega/mebi...) dynamically based on max load.
+    // - Main goal is to maximize the amount of information displayed given a
+    //   specific height. We don't want to drown out some data if the ranges are too
+    //   far though!  Nor do we want to filter out too much data...
+    // - Change the y-axis unit (kilo/kibi, mega/mebi...) dynamically based on max
+    //   load.
     //
-    // The idea is we take the top value, build our scale such that each "point" is a scaled version of that.
-    // So for example, let's say I use 390 Mb/s.  If I drew 4 segments, it would be 97.5, 195, 292.5, 390, and
+    // The idea is we take the top value, build our scale such that each "point" is
+    // a scaled version of that. So for example, let's say I use 390 Mb/s.  If I
+    // drew 4 segments, it would be 97.5, 195, 292.5, 390, and
     // probably something like 438.75?
     //
-    // So, how do we do this in ratatui?  Well, if we  are using intervals that tie in perfectly to the max
-    // value we want... then it's actually not that hard.  Since ratatui accepts a vector as labels and will
-    // properly space them all out... we just work with that and space it out properly.
+    // So, how do we do this in ratatui?  Well, if we  are using intervals that tie
+    // in perfectly to the max value we want... then it's actually not that
+    // hard.  Since ratatui accepts a vector as labels and will properly space
+    // them all out... we just work with that and space it out properly.
     //
     // Dynamic chart idea based off of FreeNAS's chart design.
     //
     // ===
     //
-    // For log data, we just use the old method of log intervals (kilo/mega/giga/etc.).  Keep it nice and simple.
+    // For log data, we just use the old method of log intervals
+    // (kilo/mega/giga/etc.).  Keep it nice and simple.
 
-    // Now just check the largest unit we correspond to... then proceed to build some entries from there!
+    // Now just check the largest unit we correspond to... then proceed to build
+    // some entries from there!
 
     let unit_char = match network_unit_type {
         DataUnit::Byte => "B",
@@ -411,8 +419,9 @@ fn adjust_network_data_point(
                     )
                 };
 
-            // Finally, build an acceptable range starting from there, using the given height!
-            // Note we try to put more of a weight on the bottom section vs. the top, since the top has less data.
+            // Finally, build an acceptable range starting from there, using the given
+            // height! Note we try to put more of a weight on the bottom section
+            // vs. the top, since the top has less data.
 
             let base_unit = max_value_scaled;
             let labels: Vec<String> = vec![
@@ -422,7 +431,8 @@ fn adjust_network_data_point(
                 format!("{:.1}", base_unit * 1.5),
             ]
             .into_iter()
-            .map(|s| format!("{s:>5}")) // Pull 5 as the longest legend value is generally going to be 5 digits (if they somehow hit over 5 terabits per second)
+            .map(|s| format!("{s:>5}")) // Pull 5 as the longest legend value is generally going to be 5 digits (if they somehow
+            // hit over 5 terabits per second)
             .collect();
 
             (bumped_max_entry, labels)
diff --git a/src/canvas/widgets/process_table.rs b/src/canvas/widgets/process_table.rs
index 0bf698d6..11fe1b1e 100644
--- a/src/canvas/widgets/process_table.rs
+++ b/src/canvas/widgets/process_table.rs
@@ -20,7 +20,8 @@ const SORT_MENU_WIDTH: u16 = 7;
 
 impl Painter {
     /// Draws and handles all process-related drawing.  Use this.
-    /// - `widget_id` here represents the widget ID of the process widget itself!
+    /// - `widget_id` here represents the widget ID of the process widget
+    ///   itself!
     pub fn draw_process(
         &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
         widget_id: u64,
@@ -106,7 +107,8 @@ impl Painter {
     }
 
     /// Draws the process search field.
-    /// - `widget_id` represents the widget ID of the search box itself --- NOT the process widget
+    /// - `widget_id` represents the widget ID of the search box itself --- NOT
+    ///   the process widget
     /// state that is stored.
     fn draw_search_field(
         &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, draw_border: bool,
@@ -310,7 +312,8 @@ impl Painter {
     }
 
     /// Draws the process sort box.
-    /// - `widget_id` represents the widget ID of the sort box itself --- NOT the process widget
+    /// - `widget_id` represents the widget ID of the sort box itself --- NOT
+    ///   the process widget
     /// state that is stored.
     fn draw_sort_table(
         &self, f: &mut Frame<'_>, app_state: &mut App, draw_loc: Rect, widget_id: u64,
diff --git a/src/constants.rs b/src/constants.rs
index e3750a4a..797de3f9 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -1,6 +1,6 @@
 use tui::widgets::Borders;
 
-use crate::options::ConfigColours;
+use crate::options::ColoursConfig;
 
 // Default widget ID
 pub const DEFAULT_WIDGET_ID: u64 = 56709;
@@ -16,7 +16,8 @@ pub const TICK_RATE_IN_MILLISECONDS: u64 = 200;
 pub const DEFAULT_REFRESH_RATE_IN_MILLISECONDS: u64 = 1000;
 pub const MAX_KEY_TIMEOUT_IN_MILLISECONDS: u64 = 1000;
 
-// Limits for when we should stop showing table gaps/labels (anything less means not shown)
+// Limits for when we should stop showing table gaps/labels (anything less means
+// not shown)
 pub const TABLE_GAP_HEIGHT_LIMIT: u16 = 7;
 pub const TIME_LABEL_HEIGHT_LIMIT: u16 = 7;
 
@@ -25,8 +26,8 @@ pub const SIDE_BORDERS: Borders = Borders::LEFT.union(Borders::RIGHT);
 
 // Colour profiles
 // TODO: Generate these with a macro or something...
-pub fn default_light_mode_colour_palette() -> ConfigColours {
-    ConfigColours {
+pub fn default_light_mode_colour_palette() -> ColoursConfig {
+    ColoursConfig {
         text_color: Some("black".into()),
         border_color: Some("black".into()),
         table_header_color: Some("black".into()),
@@ -61,12 +62,12 @@ pub fn default_light_mode_colour_palette() -> ConfigColours {
             "Blue".into(),
             "Red".into(),
         ]),
-        ..ConfigColours::default()
+        ..ColoursConfig::default()
     }
 }
 
-pub fn gruvbox_colour_palette() -> ConfigColours {
-    ConfigColours {
+pub fn gruvbox_colour_palette() -> ColoursConfig {
+    ColoursConfig {
         table_header_color: Some("#83a598".into()),
         all_cpu_color: Some("#8ec07c".into()),
         avg_cpu_color: Some("#fb4934".into()),
@@ -124,8 +125,8 @@ pub fn gruvbox_colour_palette() -> ConfigColours {
     }
 }
 
-pub fn gruvbox_light_colour_palette() -> ConfigColours {
-    ConfigColours {
+pub fn gruvbox_light_colour_palette() -> ColoursConfig {
+    ColoursConfig {
         table_header_color: Some("#076678".into()),
         all_cpu_color: Some("#8ec07c".into()),
         avg_cpu_color: Some("#fb4934".into()),
@@ -183,8 +184,8 @@ pub fn gruvbox_light_colour_palette() -> ConfigColours {
     }
 }
 
-pub fn nord_colour_palette() -> ConfigColours {
-    ConfigColours {
+pub fn nord_colour_palette() -> ColoursConfig {
+    ColoursConfig {
         table_header_color: Some("#81a1c1".into()),
         all_cpu_color: Some("#88c0d0".into()),
         avg_cpu_color: Some("#8fbcbb".into()),
@@ -230,8 +231,8 @@ pub fn nord_colour_palette() -> ConfigColours {
     }
 }
 
-pub fn nord_light_colour_palette() -> ConfigColours {
-    ConfigColours {
+pub fn nord_light_colour_palette() -> ColoursConfig {
+    ColoursConfig {
         table_header_color: Some("#5e81ac".into()),
         all_cpu_color: Some("#81a1c1".into()),
         avg_cpu_color: Some("#8fbcbb".into()),
@@ -601,16 +602,51 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom.  Al
 # 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 = "TopRight".
 
-# These are flags around the process widget.
+# Processes widget configuration
 #[processes]
 # The columns shown by the process widget. The following columns are supported:
-#   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%"]
 
-# [cpu]
+# CPU widget configuration
+#[cpu]
 # One of "all" (default), "average"/"avg"
 # default = "average"
 
+# Disk widget configuration
+#[disk]
+#[disk.name_filter]
+#is_list_ignored = true
+#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
+#regex = true
+#case_sensitive = false
+#whole_word = false
+
+#[disk.mount_filter]
+#is_list_ignored = true
+#list = ["/mnt/.*", "/boot"]
+#regex = true
+#case_sensitive = false
+#whole_word = false
+
+# Temperature widget configuration
+#[temperature]
+#[temperature.sensor_filter]
+#is_list_ignored = true
+#list = ["cpu", "wifi"]
+#regex = false
+#case_sensitive = false
+#whole_word = false
+
+# Network widget configuration
+#[network]
+#[network.interface_filter]
+#is_list_ignored = true
+#list = ["virbr0.*"]
+#regex = true
+#case_sensitive = false
+#whole_word = false
+
 # These are all the components that support custom theming.  Note that colour support
 # will depend on terminal support.
 #[colors] # Uncomment if you want to use custom colors
@@ -681,37 +717,6 @@ pub const CONFIG_TEXT: &str = r#"# This is a default config file for bottom.  Al
 #  [[row.child]]
 #    type="proc"
 #    default=true
-
-
-# Filters - you can hide specific temperature sensors, network interfaces, and disks using filters.  This is admittedly
-# a bit hard to use as of now, and there is a planned in-app interface for managing this in the future:
-#[disk_filter]
-#is_list_ignored = true
-#list = ["/dev/sda\\d+", "/dev/nvme0n1p2"]
-#regex = true
-#case_sensitive = false
-#whole_word = false
-
-#[mount_filter]
-#is_list_ignored = true
-#list = ["/mnt/.*", "/boot"]
-#regex = true
-#case_sensitive = false
-#whole_word = false
-
-#[temp_filter]
-#is_list_ignored = true
-#list = ["cpu", "wifi"]
-#regex = false
-#case_sensitive = false
-#whole_word = false
-
-#[net_filter]
-#is_list_ignored = true
-#list = ["virbr0.*"]
-#regex = true
-#case_sensitive = false
-#whole_word = false
 "#;
 
 pub const CONFIG_TOP_HEAD: &str = r##"# This is bottom's config file.
@@ -773,8 +778,8 @@ mod test {
         }
     }
 
-    /// This test exists because previously, [`SIDE_BORDERS`] was set incorrectly after I moved from
-    /// tui-rs to ratatui.
+    /// This test exists because previously, [`SIDE_BORDERS`] was set
+    /// incorrectly after I moved from tui-rs to ratatui.
     #[test]
     fn assert_side_border_bits_match() {
         assert_eq!(
diff --git a/src/data_collection.rs b/src/data_collection.rs
index 3095405a..45c8fe0a 100644
--- a/src/data_collection.rs
+++ b/src/data_collection.rs
@@ -98,7 +98,8 @@ impl Data {
     }
 }
 
-/// A wrapper around the sysinfo data source. We use sysinfo for the following data:
+/// A wrapper around the sysinfo data source. We use sysinfo for the following
+/// data:
 /// - CPU usage
 /// - Memory usage
 /// - Network usage
@@ -183,7 +184,7 @@ impl DataCollector {
             temperature_type: TemperatureType::Celsius,
             use_current_cpu_total: false,
             unnormalized_cpu: false,
-            last_collection_time: Instant::now() - Duration::from_secs(600), // Initialize it to the past to force it to load on initialization.
+            last_collection_time: Instant::now() - Duration::from_secs(600), /* Initialize it to the past to force it to load on initialization. */
             total_rx: 0,
             total_tx: 0,
             show_average_cpu: false,
@@ -255,7 +256,8 @@ impl DataCollector {
     /// - Disk (Windows)
     /// - Temperatures (non-Linux)
     fn refresh_sysinfo_data(&mut self) {
-        // Refresh the list of objects once every minute. If it's too frequent it can cause segfaults.
+        // Refresh the list of objects once every minute. If it's too frequent it can
+        // cause segfaults.
         const LIST_REFRESH_TIME: Duration = Duration::from_secs(60);
         let refresh_start = Instant::now();
 
@@ -374,9 +376,9 @@ impl DataCollector {
     fn update_processes(&mut self) {
         if self.widgets_to_harvest.use_proc {
             if let Ok(mut process_list) = self.get_processes() {
-                // NB: To avoid duplicate sorts on rerenders/events, we sort the processes by PID here.
-                // We also want to avoid re-sorting *again* later on if we're sorting by PID, since we already
-                // did it here!
+                // NB: To avoid duplicate sorts on rerenders/events, we sort the processes by
+                // PID here. We also want to avoid re-sorting *again* later on
+                // if we're sorting by PID, since we already did it here!
                 process_list.sort_unstable_by_key(|p| p.pid);
                 self.data.list_of_processes = Some(process_list);
             }
@@ -473,12 +475,15 @@ impl DataCollector {
     }
 }
 
-/// We set a sleep duration between 10ms and 250ms, ideally sysinfo's [`sysinfo::MINIMUM_CPU_UPDATE_INTERVAL`] + 1.
+/// We set a sleep duration between 10ms and 250ms, ideally sysinfo's
+/// [`sysinfo::MINIMUM_CPU_UPDATE_INTERVAL`] + 1.
 ///
-/// We bound the upper end to avoid waiting too long (e.g. FreeBSD is 1s, which I'm fine with losing
-/// accuracy on for the first refresh), and we bound the lower end just to avoid the off-chance that
-/// refreshing too quickly causes problems. This second case should only happen on unsupported
-/// systems via sysinfo, in which case [`sysinfo::MINIMUM_CPU_UPDATE_INTERVAL`] is defined as 0.
+/// We bound the upper end to avoid waiting too long (e.g. FreeBSD is 1s, which
+/// I'm fine with losing accuracy on for the first refresh), and we bound the
+/// lower end just to avoid the off-chance that refreshing too quickly causes
+/// problems. This second case should only happen on unsupported systems via
+/// sysinfo, in which case [`sysinfo::MINIMUM_CPU_UPDATE_INTERVAL`] is defined
+/// as 0.
 ///
 /// We also do `INTERVAL + 1` for some wiggle room, just in case.
 const fn get_sleep_duration() -> Duration {
diff --git a/src/data_collection/batteries.rs b/src/data_collection/batteries.rs
index 8c0e4a92..a155ad2d 100644
--- a/src/data_collection/batteries.rs
+++ b/src/data_collection/batteries.rs
@@ -1,6 +1,7 @@
 //! Data collection for batteries.
 //!
-//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by the battery crate.
+//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by
+//! the battery crate.
 
 cfg_if::cfg_if! {
     if #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "ios"))] {
diff --git a/src/data_collection/cpu/sysinfo.rs b/src/data_collection/cpu/sysinfo.rs
index 196e8a87..707822f9 100644
--- a/src/data_collection/cpu/sysinfo.rs
+++ b/src/data_collection/cpu/sysinfo.rs
@@ -32,7 +32,8 @@ pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> crate::error::
 }
 
 pub fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> {
-    // The API for sysinfo apparently wants you to call it like this, rather than using a &System.
+    // The API for sysinfo apparently wants you to call it like this, rather than
+    // using a &System.
     let LoadAvg { one, five, fifteen } = sysinfo::System::load_average();
 
     Ok([one as f32, five as f32, fifteen as f32])
diff --git a/src/data_collection/disks.rs b/src/data_collection/disks.rs
index 8d55de98..9aba0579 100644
--- a/src/data_collection/disks.rs
+++ b/src/data_collection/disks.rs
@@ -86,16 +86,18 @@ cfg_if! {
     }
 }
 
-/// Whether to keep the current disk entry given the filters, disk name, and disk mount.
-/// Precedence ordering in the case where name and mount filters disagree, "allow"
-/// takes precedence over "deny".
+/// Whether to keep the current disk entry given the filters, disk name, and
+/// disk mount. Precedence ordering in the case where name and mount filters
+/// disagree, "allow" takes precedence over "deny".
 ///
 /// For implementation, we do this as follows:
 ///
-/// 1. Is the entry allowed through any filter? That is, does it match an entry in a
-///    filter where `is_list_ignored` is `false`? If so, we always keep this entry.
-/// 2. Is the entry denied through any filter? That is, does it match an entry in a
-///    filter where `is_list_ignored` is `true`? If so, we always deny this entry.
+/// 1. Is the entry allowed through any filter? That is, does it match an entry
+///    in a filter where `is_list_ignored` is `false`? If so, we always keep
+///    this entry.
+/// 2. Is the entry denied through any filter? That is, does it match an entry
+///    in a filter where `is_list_ignored` is `true`? If so, we always deny this
+///    entry.
 /// 3. Anything else is allowed.
 pub fn keep_disk_entry(
     disk_name: &str, mount_point: &str, disk_filter: &Option<Filter>, mount_filter: &Option<Filter>,
diff --git a/src/data_collection/disks/freebsd.rs b/src/data_collection/disks/freebsd.rs
index 2d16e1c5..095c5b12 100644
--- a/src/data_collection/disks/freebsd.rs
+++ b/src/data_collection/disks/freebsd.rs
@@ -28,7 +28,8 @@ struct FileSystem {
 }
 
 pub fn get_io_usage() -> error::Result<IoHarvest> {
-    // TODO: Should this (and other I/O collectors) fail fast? In general, should collection ever fail fast?
+    // TODO: Should this (and other I/O collectors) fail fast? In general, should
+    // collection ever fail fast?
     #[allow(unused_mut)]
     let mut io_harvest: HashMap<String, Option<IoData>> =
         get_disk_info().map(|storage_system_information| {
diff --git a/src/data_collection/disks/unix.rs b/src/data_collection/disks/unix.rs
index 309a0a19..3178f94c 100644
--- a/src/data_collection/disks/unix.rs
+++ b/src/data_collection/disks/unix.rs
@@ -1,5 +1,5 @@
-//! Disk stats for Unix-like systems that aren't supported through other means. Officially,
-//! for now, this means Linux and macOS.
+//! Disk stats for Unix-like systems that aren't supported through other means.
+//! Officially, for now, this means Linux and macOS.
 
 mod file_systems;
 
@@ -37,16 +37,21 @@ pub fn get_disk_usage(collector: &DataCollector) -> anyhow::Result<Vec<DiskHarve
         let name = partition.get_device_name();
         let mount_point = partition.mount_point().to_string_lossy().to_string();
 
-        // Precedence ordering in the case where name and mount filters disagree, "allow" takes precedence over "deny".
+        // Precedence ordering in the case where name and mount filters disagree,
+        // "allow" takes precedence over "deny".
         //
         // For implementation, we do this as follows:
-        // 1. Is the entry allowed through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `false`? If so, we always keep this entry.
-        // 2. Is the entry denied through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `true`? If so, we always deny this entry.
+        // 1. Is the entry allowed through any filter? That is, does it match an entry
+        //    in a filter where `is_list_ignored` is `false`? If so, we always keep this
+        //    entry.
+        // 2. Is the entry denied through any filter? That is, does it match an entry in
+        //    a filter where `is_list_ignored` is `true`? If so, we always deny this
+        //    entry.
         // 3. Anything else is allowed.
 
         if keep_disk_entry(&name, &mount_point, disk_filter, mount_filter) {
-            // The usage line can fail in some cases (for example, if you use Void Linux + LUKS,
-            // see https://github.com/ClementTsang/bottom/issues/419 for details).
+            // The usage line can fail in some cases (for example, if you use Void Linux +
+            // LUKS, see https://github.com/ClementTsang/bottom/issues/419 for details).
             if let Ok(usage) = partition.usage() {
                 let total = usage.total();
 
diff --git a/src/data_collection/disks/unix/file_systems.rs b/src/data_collection/disks/unix/file_systems.rs
index 713754e2..39ac0c51 100644
--- a/src/data_collection/disks/unix/file_systems.rs
+++ b/src/data_collection/disks/unix/file_systems.rs
@@ -5,8 +5,8 @@ use crate::multi_eq_ignore_ascii_case;
 /// Known filesystems. Original list from
 /// [heim](https://github.com/heim-rs/heim/blob/master/heim-disk/src/filesystem.rs).
 ///
-/// All physical filesystems should have their own enum element and all virtual filesystems will go into
-/// the [`FileSystem::Other`] element.
+/// All physical filesystems should have their own enum element and all virtual
+/// filesystems will go into the [`FileSystem::Other`] element.
 #[derive(Debug, Eq, PartialEq, Hash, Clone)]
 #[non_exhaustive]
 pub enum FileSystem {
@@ -81,7 +81,8 @@ impl FileSystem {
         !self.is_virtual()
     }
 
-    /// Checks if filesystem is used for a virtual devices (such as `tmpfs` or `smb` mounts).
+    /// Checks if filesystem is used for a virtual devices (such as `tmpfs` or
+    /// `smb` mounts).
     #[inline]
     pub fn is_virtual(&self) -> bool {
         matches!(self, FileSystem::Other(..))
diff --git a/src/data_collection/disks/unix/linux/counters.rs b/src/data_collection/disks/unix/linux/counters.rs
index bc2cb076..d0da4c82 100644
--- a/src/data_collection/disks/unix/linux/counters.rs
+++ b/src/data_collection/disks/unix/linux/counters.rs
@@ -28,7 +28,8 @@ impl FromStr for IoCounters {
 
     /// Converts a `&str` to an [`IoCounters`].
     ///
-    /// Follows the format used in Linux 2.6+. Note that this completely ignores the following stats:
+    /// Follows the format used in Linux 2.6+. Note that this completely ignores
+    /// the following stats:
     /// - Discard stats from 4.18+
     /// - Flush stats from 5.5+
     ///
@@ -71,7 +72,8 @@ pub fn io_stats() -> anyhow::Result<Vec<IoCounters>> {
     let mut reader = BufReader::new(File::open(PROC_DISKSTATS)?);
     let mut line = String::new();
 
-    // This saves us from doing a string allocation on each iteration compared to `lines()`.
+    // This saves us from doing a string allocation on each iteration compared to
+    // `lines()`.
     while let Ok(bytes) = reader.read_line(&mut line) {
         if bytes > 0 {
             if let Ok(counters) = IoCounters::from_str(&line) {
diff --git a/src/data_collection/disks/unix/linux/partition.rs b/src/data_collection/disks/unix/linux/partition.rs
index 488ad8d9..5da832a0 100644
--- a/src/data_collection/disks/unix/linux/partition.rs
+++ b/src/data_collection/disks/unix/linux/partition.rs
@@ -43,8 +43,8 @@ impl Partition {
     /// Returns the device name for the partition.
     pub fn get_device_name(&self) -> String {
         if let Some(device) = self.device() {
-            // See if this disk is actually mounted elsewhere on Linux. This is a workaround properly map I/O
-            // in some cases (i.e. disk encryption, https://github.com/ClementTsang/bottom/issues/419).
+            // See if this disk is actually mounted elsewhere on Linux. This is a workaround
+            // properly map I/O in some cases (i.e. disk encryption, https://github.com/ClementTsang/bottom/issues/419).
             if let Ok(path) = std::fs::read_link(device) {
                 if path.is_absolute() {
                     path.into_os_string()
@@ -87,7 +87,8 @@ impl Partition {
 
         let mut vfs = mem::MaybeUninit::<libc::statvfs>::uninit();
 
-        // SAFETY: libc call, `path` is a valid C string and buf is a valid pointer to write to.
+        // SAFETY: libc call, `path` is a valid C string and buf is a valid pointer to
+        // write to.
         let result = unsafe { libc::statvfs(path.as_ptr(), vfs.as_mut_ptr()) };
 
         if result == 0 {
@@ -146,7 +147,8 @@ pub(crate) fn partitions() -> anyhow::Result<Vec<Partition>> {
     let mut reader = BufReader::new(File::open(PROC_MOUNTS)?);
     let mut line = String::new();
 
-    // This saves us from doing a string allocation on each iteration compared to `lines()`.
+    // This saves us from doing a string allocation on each iteration compared to
+    // `lines()`.
     while let Ok(bytes) = reader.read_line(&mut line) {
         if bytes > 0 {
             if let Ok(partition) = Partition::from_str(&line) {
@@ -171,7 +173,8 @@ pub(crate) fn physical_partitions() -> anyhow::Result<Vec<Partition>> {
     let mut reader = BufReader::new(File::open(PROC_MOUNTS)?);
     let mut line = String::new();
 
-    // This saves us from doing a string allocation on each iteration compared to `lines()`.
+    // This saves us from doing a string allocation on each iteration compared to
+    // `lines()`.
     while let Ok(bytes) = reader.read_line(&mut line) {
         if bytes > 0 {
             if let Ok(partition) = Partition::from_str(&line) {
diff --git a/src/data_collection/disks/unix/macos/counters.rs b/src/data_collection/disks/unix/macos/counters.rs
index 1f03b297..7d3d6768 100644
--- a/src/data_collection/disks/unix/macos/counters.rs
+++ b/src/data_collection/disks/unix/macos/counters.rs
@@ -10,23 +10,24 @@ fn get_device_io(device: io_kit::IoObject) -> anyhow::Result<IoCounters> {
     //
     // Okay, so this is weird.
     //
-    // The problem is that if I have this check - this is what sources like psutil use, for
-    // example (see https://github.com/giampaolo/psutil/blob/7eadee31db2f038763a3a6f978db1ea76bbc4674/psutil/_psutil_osx.c#LL1422C20-L1422C20)
+    // The problem is that if I have this check - this is what sources like psutil
+    // use, for example (see https://github.com/giampaolo/psutil/blob/7eadee31db2f038763a3a6f978db1ea76bbc4674/psutil/_psutil_osx.c#LL1422C20-L1422C20)
     // then this will only return stuff like disk0.
     //
-    // The problem with this is that there is *never* a disk0 *disk* entry to correspond to this,
-    // so there will be entries like disk1 or whatnot. Someone's done some digging on the gopsutil
-    // repo (https://github.com/shirou/gopsutil/issues/855#issuecomment-610016435), and it seems
+    // The problem with this is that there is *never* a disk0 *disk* entry to
+    // correspond to this, so there will be entries like disk1 or whatnot.
+    // Someone's done some digging on the gopsutil repo (https://github.com/shirou/gopsutil/issues/855#issuecomment-610016435), and it seems
     // like this is a consequence of how Apple does logical volumes.
     //
-    // So with all that said, what I've found is that I *can* still get a mapping - but I have
-    // to disable the conform check, which... is weird. I'm not sure if this is valid at all. But
-    // it *does* seem to match Activity Monitor with regards to disk activity, so... I guess we
-    // can leave this for now...?
+    // So with all that said, what I've found is that I *can* still get a mapping -
+    // but I have to disable the conform check, which... is weird. I'm not sure
+    // if this is valid at all. But it *does* seem to match Activity Monitor
+    // with regards to disk activity, so... I guess we can leave this for
+    // now...?
 
     // if !parent.conforms_to_block_storage_driver() {
-    //     anyhow::bail!("{parent:?}, the parent of {device:?} does not conform to IOBlockStorageDriver")
-    // }
+    //     anyhow::bail!("{parent:?}, the parent of {device:?} does not conform to
+    // IOBlockStorageDriver") }
 
     let disk_props = device.properties()?;
     let parent_props = parent.properties()?;
diff --git a/src/data_collection/disks/unix/macos/io_kit/bindings.rs b/src/data_collection/disks/unix/macos/io_kit/bindings.rs
index 36576a60..93aaf9b8 100644
--- a/src/data_collection/disks/unix/macos/io_kit/bindings.rs
+++ b/src/data_collection/disks/unix/macos/io_kit/bindings.rs
@@ -49,7 +49,8 @@ extern "C" {
         entry: io_registry_entry_t, plane: *const libc::c_char, parent: *mut io_registry_entry_t,
     ) -> kern_return_t;
 
-    // pub fn IOObjectConformsTo(object: io_object_t, className: *const libc::c_char) -> mach2::boolean::boolean_t;
+    // pub fn IOObjectConformsTo(object: io_object_t, className: *const
+    // libc::c_char) -> mach2::boolean::boolean_t;
 
     pub fn IORegistryEntryCreateCFProperties(
         entry: io_registry_entry_t, properties: *mut CFMutableDictionaryRef,
diff --git a/src/data_collection/disks/unix/macos/io_kit/io_iterator.rs b/src/data_collection/disks/unix/macos/io_kit/io_iterator.rs
index 1f440466..b1b1f6a7 100644
--- a/src/data_collection/disks/unix/macos/io_kit/io_iterator.rs
+++ b/src/data_collection/disks/unix/macos/io_kit/io_iterator.rs
@@ -37,7 +37,8 @@ impl Iterator for IoIterator {
     fn next(&mut self) -> Option<Self::Item> {
         // Basically, we just stop when we hit 0.
 
-        // SAFETY: IOKit call, the passed argument (an `io_iterator_t`) is what is expected.
+        // SAFETY: IOKit call, the passed argument (an `io_iterator_t`) is what is
+        // expected.
         match unsafe { IOIteratorNext(self.0) } {
             0 => None,
             io_object => Some(IoObject::from(io_object)),
@@ -47,7 +48,8 @@ impl Iterator for IoIterator {
 
 impl Drop for IoIterator {
     fn drop(&mut self) {
-        // SAFETY: IOKit call, the passed argument (an `io_iterator_t`) is what is expected.
+        // SAFETY: IOKit call, the passed argument (an `io_iterator_t`) is what is
+        // expected.
         let result = unsafe { IOObjectRelease(self.0) };
         assert_eq!(result, kern_return::KERN_SUCCESS);
     }
diff --git a/src/data_collection/disks/unix/macos/io_kit/io_object.rs b/src/data_collection/disks/unix/macos/io_kit/io_object.rs
index af110f0c..f7aa43ab 100644
--- a/src/data_collection/disks/unix/macos/io_kit/io_object.rs
+++ b/src/data_collection/disks/unix/macos/io_kit/io_object.rs
@@ -24,8 +24,9 @@ pub struct IoObject(io_object_t);
 impl IoObject {
     /// Returns a typed dictionary with this object's properties.
     pub fn properties(&self) -> anyhow::Result<CFDictionary<CFString, CFType>> {
-        // SAFETY: The IOKit call should be fine, the arguments are safe. The `assume_init` should also be fine, as
-        // we guard against it with a check against `result` to ensure it succeeded.
+        // SAFETY: The IOKit call should be fine, the arguments are safe. The
+        // `assume_init` should also be fine, as we guard against it with a
+        // check against `result` to ensure it succeeded.
         unsafe {
             let mut props = mem::MaybeUninit::<CFMutableDictionaryRef>::uninit();
 
@@ -45,8 +46,8 @@ impl IoObject {
         }
     }
 
-    /// Gets the [`kIOServicePlane`] parent [`io_object_t`] for this [`io_object_t`], if there
-    /// is one.
+    /// Gets the [`kIOServicePlane`] parent [`io_object_t`] for this
+    /// [`io_object_t`], if there is one.
     pub fn service_parent(&self) -> anyhow::Result<IoObject> {
         let mut parent: io_registry_entry_t = 0;
 
@@ -65,7 +66,8 @@ impl IoObject {
     // pub fn conforms_to_block_storage_driver(&self) -> bool {
     //     // SAFETY: IOKit call, the arguments should be safe.
     //     let result =
-    //         unsafe { IOObjectConformsTo(self.0, "IOBlockStorageDriver\0".as_ptr().cast()) };
+    //         unsafe { IOObjectConformsTo(self.0,
+    // "IOBlockStorageDriver\0".as_ptr().cast()) };
 
     //     result != 0
     // }
@@ -79,7 +81,8 @@ impl From<io_object_t> for IoObject {
 
 impl Drop for IoObject {
     fn drop(&mut self) {
-        // SAFETY: IOKit call, the argument here (an `io_object_t`) should be safe and expected.
+        // SAFETY: IOKit call, the argument here (an `io_object_t`) should be safe and
+        // expected.
         let result = unsafe { IOObjectRelease(self.0) };
         assert_eq!(result, kern_return::KERN_SUCCESS);
     }
diff --git a/src/data_collection/disks/unix/other/bindings.rs b/src/data_collection/disks/unix/other/bindings.rs
index 0dd4b4fb..3c5739cc 100644
--- a/src/data_collection/disks/unix/other/bindings.rs
+++ b/src/data_collection/disks/unix/other/bindings.rs
@@ -34,8 +34,9 @@ pub(crate) fn mounts() -> anyhow::Result<Vec<libc::statfs>> {
             "Expected {expected_len} statfs entries, but instead got {result} entries",
         );
 
-        // SAFETY: We have a debug assert check, and if `result` is not correct (-1), we check against it.
-        // Otherwise, getfsstat64 should return the number of statfs structures if it succeeded.
+        // SAFETY: We have a debug assert check, and if `result` is not correct (-1), we
+        // check against it. Otherwise, getfsstat64 should return the number of
+        // statfs structures if it succeeded.
         //
         // Source: https://man.freebsd.org/cgi/man.cgi?query=getfsstat&sektion=2&format=html
         unsafe {
diff --git a/src/data_collection/disks/unix/other/partition.rs b/src/data_collection/disks/unix/other/partition.rs
index 1e64a70d..ad97bdb4 100644
--- a/src/data_collection/disks/unix/other/partition.rs
+++ b/src/data_collection/disks/unix/other/partition.rs
@@ -38,7 +38,8 @@ impl Partition {
         let result = unsafe { libc::statvfs(path.as_ptr(), vfs.as_mut_ptr()) };
 
         if result == 0 {
-            // SAFETY: We check that it succeeded (result is 0), which means vfs should be populated.
+            // SAFETY: We check that it succeeded (result is 0), which means vfs should be
+            // populated.
             Ok(Usage::new(unsafe { vfs.assume_init() }))
         } else {
             bail!("statvfs failed to get the disk usage for disk {path:?}")
diff --git a/src/data_collection/disks/unix/usage.rs b/src/data_collection/disks/unix/usage.rs
index 3ee70bc4..8b78edb2 100644
--- a/src/data_collection/disks/unix/usage.rs
+++ b/src/data_collection/disks/unix/usage.rs
@@ -1,7 +1,7 @@
 pub struct Usage(libc::statvfs);
 
-// Note that x86 returns `u32` values while x86-64 returns `u64`s, so we convert everything
-// to `u64` for consistency.
+// Note that x86 returns `u32` values while x86-64 returns `u64`s, so we convert
+// everything to `u64` for consistency.
 #[allow(clippy::useless_conversion)]
 impl Usage {
     pub(crate) fn new(vfs: libc::statvfs) -> Self {
@@ -13,19 +13,22 @@ impl Usage {
         u64::from(self.0.f_blocks) * u64::from(self.0.f_frsize)
     }
 
-    /// Returns the available number of bytes used. Note this is not necessarily the same as [`Usage::free`].
+    /// Returns the available number of bytes used. Note this is not necessarily
+    /// the same as [`Usage::free`].
     pub fn available(&self) -> u64 {
         u64::from(self.0.f_bfree) * u64::from(self.0.f_frsize)
     }
 
     #[allow(dead_code)]
-    /// Returns the total number of bytes used. Equal to `total - available` on Unix.
+    /// Returns the total number of bytes used. Equal to `total - available` on
+    /// Unix.
     pub fn used(&self) -> u64 {
         let avail_to_root = u64::from(self.0.f_bfree) * u64::from(self.0.f_frsize);
         self.total() - avail_to_root
     }
 
-    /// Returns the total number of bytes free. Note this is not necessarily the same as [`Usage::available`].
+    /// Returns the total number of bytes free. Note this is not necessarily the
+    /// same as [`Usage::available`].
     pub fn free(&self) -> u64 {
         u64::from(self.0.f_bavail) * u64::from(self.0.f_frsize)
     }
diff --git a/src/data_collection/disks/windows/bindings.rs b/src/data_collection/disks/windows/bindings.rs
index c5436145..f5c6b37e 100644
--- a/src/data_collection/disks/windows/bindings.rs
+++ b/src/data_collection/disks/windows/bindings.rs
@@ -41,7 +41,8 @@ fn volume_io(volume: &Path) -> anyhow::Result<DISK_PERFORMANCE> {
         wide_path
     };
 
-    // SAFETY: API call, arguments should be correct. We must also check after the call to ensure it is valid.
+    // SAFETY: API call, arguments should be correct. We must also check after the
+    // call to ensure it is valid.
     let h_device = unsafe {
         CreateFileW(
             windows::core::PCWSTR(volume.as_ptr()),
@@ -61,7 +62,8 @@ fn volume_io(volume: &Path) -> anyhow::Result<DISK_PERFORMANCE> {
     let mut disk_performance = DISK_PERFORMANCE::default();
     let mut bytes_returned = 0;
 
-    // SAFETY: This should be safe, we'll manually check the results and the arguments should be valid.
+    // SAFETY: This should be safe, we'll manually check the results and the
+    // arguments should be valid.
     let ret = unsafe {
         DeviceIoControl(
             h_device,
@@ -112,7 +114,8 @@ pub(crate) fn all_volume_io() -> anyhow::Result<Vec<anyhow::Result<(DISK_PERFORM
     let mut buffer = [0_u16; Foundation::MAX_PATH as usize];
 
     // Get the first volume and add the stats needed.
-    // SAFETY: We must verify the handle is correct. If no volume is found, it will be set to `INVALID_HANDLE_VALUE`.
+    // SAFETY: We must verify the handle is correct. If no volume is found, it will
+    // be set to `INVALID_HANDLE_VALUE`.
     let handle = unsafe { FindFirstVolumeW(&mut buffer) }?;
     if handle.is_invalid() {
         bail!("Invalid handle value: {:?}", io::Error::last_os_error());
@@ -148,8 +151,8 @@ pub(crate) fn all_volume_io() -> anyhow::Result<Vec<anyhow::Result<(DISK_PERFORM
 
 /// Returns the volume name from a mount name if possible.
 pub(crate) fn volume_name_from_mount(mount: &str) -> anyhow::Result<String> {
-    // According to winapi docs 50 is a reasonable length to accomodate the volume path
-    // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumenameforvolumemountpointw
+    // According to winapi docs 50 is a reasonable length to accomodate the volume
+    // path https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumenameforvolumemountpointw
     const VOLUME_MAX_LEN: usize = 50;
 
     let mount = {
diff --git a/src/data_collection/disks/zfs_io_counters.rs b/src/data_collection/disks/zfs_io_counters.rs
index a577915a..cb15207b 100644
--- a/src/data_collection/disks/zfs_io_counters.rs
+++ b/src/data_collection/disks/zfs_io_counters.rs
@@ -1,6 +1,7 @@
 use crate::data_collection::disks::IoCounters;
 
-/// Returns zpool I/O stats. Pulls data from `sysctl kstat.zfs.{POOL}.dataset.{objset-*}`
+/// Returns zpool I/O stats. Pulls data from `sysctl
+/// kstat.zfs.{POOL}.dataset.{objset-*}`
 #[cfg(target_os = "freebsd")]
 pub fn zfs_io_stats() -> anyhow::Result<Vec<IoCounters>> {
     use sysctl::Sysctl;
diff --git a/src/data_collection/memory.rs b/src/data_collection/memory.rs
index 98cfebda..b953e2bf 100644
--- a/src/data_collection/memory.rs
+++ b/src/data_collection/memory.rs
@@ -19,5 +19,6 @@ pub mod arc;
 pub struct MemHarvest {
     pub used_bytes: u64,
     pub total_bytes: u64,
-    pub use_percent: Option<f64>, // TODO: Might be find to just make this an f64, and any consumer checks NaN.
+    pub use_percent: Option<f64>, /* TODO: Might be find to just make this an f64, and any
+                                   * consumer checks NaN. */
 }
diff --git a/src/data_collection/memory/sysinfo.rs b/src/data_collection/memory/sysinfo.rs
index 330bb9b0..1f5606de 100644
--- a/src/data_collection/memory/sysinfo.rs
+++ b/src/data_collection/memory/sysinfo.rs
@@ -36,11 +36,12 @@ pub(crate) fn get_swap_usage(sys: &System) -> Option<MemHarvest> {
     })
 }
 
-/// Returns cache usage. sysinfo has no way to do this directly but it should equal the difference
-/// between the available and free memory. Free memory is defined as memory not containing any data,
-/// which means cache and buffer memory are not "free". Available memory is defined as memory able
-/// to be allocated by processes, which includes cache and buffer memory. On Windows, this will
-/// always be 0. For more information, see [docs](https://docs.rs/sysinfo/latest/sysinfo/struct.System.html#method.available_memory)
+/// Returns cache usage. sysinfo has no way to do this directly but it should
+/// equal the difference between the available and free memory. Free memory is
+/// defined as memory not containing any data, which means cache and buffer
+/// memory are not "free". Available memory is defined as memory able
+/// to be allocated by processes, which includes cache and buffer memory. On
+/// Windows, this will always be 0. For more information, see [docs](https://docs.rs/sysinfo/latest/sysinfo/struct.System.html#method.available_memory)
 /// and [memory explanation](https://askubuntu.com/questions/867068/what-is-available-memory-while-using-free-command)
 #[cfg(not(target_os = "windows"))]
 pub(crate) fn get_cache_usage(sys: &System) -> Option<MemHarvest> {
diff --git a/src/data_collection/network/sysinfo.rs b/src/data_collection/network/sysinfo.rs
index 3949c4b8..f29180b0 100644
--- a/src/data_collection/network/sysinfo.rs
+++ b/src/data_collection/network/sysinfo.rs
@@ -7,7 +7,8 @@ use sysinfo::Networks;
 use super::NetworkHarvest;
 use crate::app::filter::Filter;
 
-// TODO: Eventually make it so that this thing also takes individual usage into account, so we can show per-interface!
+// TODO: Eventually make it so that this thing also takes individual usage into
+// account, so we can show per-interface!
 pub fn get_network_data(
     networks: &Networks, prev_net_access_time: Instant, prev_net_rx: &mut u64,
     prev_net_tx: &mut u64, curr_time: Instant, filter: &Option<Filter>,
diff --git a/src/data_collection/processes.rs b/src/data_collection/processes.rs
index 52caabb7..7359ede2 100644
--- a/src/data_collection/processes.rs
+++ b/src/data_collection/processes.rs
@@ -87,7 +87,8 @@ pub struct ProcessHarvest {
     /// Cumulative process uptime.
     pub time: Duration,
 
-    /// This is the *effective* user ID of the process. This is only used on Unix platforms.
+    /// This is the *effective* user ID of the process. This is only used on
+    /// Unix platforms.
     #[cfg(target_family = "unix")]
     pub uid: Option<libc::uid_t>,
 
diff --git a/src/data_collection/processes/linux.rs b/src/data_collection/processes/linux.rs
index 899963d7..87a3a21d 100644
--- a/src/data_collection/processes/linux.rs
+++ b/src/data_collection/processes/linux.rs
@@ -29,11 +29,11 @@ pub struct PrevProcDetails {
     cpu_time: u64,
 }
 
-/// Given `/proc/stat` file contents, determine the idle and non-idle values of the CPU
-/// used to calculate CPU usage.
+/// Given `/proc/stat` file contents, determine the idle and non-idle values of
+/// the CPU used to calculate CPU usage.
 fn fetch_cpu_usage(line: &str) -> (f64, f64) {
-    /// Converts a `Option<&str>` value to an f64. If it fails to parse or is `None`, it
-    /// will return `0_f64`.
+    /// Converts a `Option<&str>` value to an f64. If it fails to parse or is
+    /// `None`, it will return `0_f64`.
     fn str_to_f64(val: Option<&str>) -> f64 {
         val.and_then(|v| v.parse::<f64>().ok()).unwrap_or(0_f64)
     }
@@ -48,8 +48,8 @@ fn fetch_cpu_usage(line: &str) -> (f64, f64) {
     let softirq: f64 = str_to_f64(val.next());
     let steal: f64 = str_to_f64(val.next());
 
-    // Note we do not get guest/guest_nice, as they are calculated as part of user/nice respectively
-    // See https://github.com/htop-dev/htop/blob/main/linux/LinuxProcessList.c
+    // Note we do not get guest/guest_nice, as they are calculated as part of
+    // user/nice respectively See https://github.com/htop-dev/htop/blob/main/linux/LinuxProcessList.c
     let idle = idle + iowait;
     let non_idle = user + nice + system + irq + softirq + steal;
 
@@ -331,8 +331,8 @@ pub(crate) fn linux_process_data(
         if unnormalized_cpu {
             let num_processors = collector.sys.system.cpus().len() as f64;
 
-            // Note we *divide* here because the later calculation divides `cpu_usage` - in effect,
-            // multiplying over the number of cores.
+            // Note we *divide* here because the later calculation divides `cpu_usage` - in
+            // effect, multiplying over the number of cores.
             cpu_usage /= num_processors;
         }
 
diff --git a/src/data_collection/processes/linux/process.rs b/src/data_collection/processes/linux/process.rs
index d77ff1f7..45ed80a6 100644
--- a/src/data_collection/processes/linux/process.rs
+++ b/src/data_collection/processes/linux/process.rs
@@ -29,7 +29,8 @@ fn next_part<'a>(iter: &mut impl Iterator<Item = &'a str>) -> Result<&'a str, io
 /// A wrapper around the data in `/proc/<PID>/stat`. For documentation, see
 /// [here](https://man7.org/linux/man-pages/man5/proc.5.html).
 ///
-/// Note this does not necessarily get all fields, only the ones we use in bottom.
+/// Note this does not necessarily get all fields, only the ones we use in
+/// bottom.
 pub(crate) struct Stat {
     /// The filename of the executable without parentheses.
     pub comm: String,
@@ -40,13 +41,16 @@ pub(crate) struct Stat {
     /// The parent process PID.
     pub ppid: Pid,
 
-    /// The amount of time this process has been scheduled in user mode in clock ticks.
+    /// The amount of time this process has been scheduled in user mode in clock
+    /// ticks.
     pub utime: u64,
 
-    /// The amount of time this process has been scheduled in kernel mode in clock ticks.
+    /// The amount of time this process has been scheduled in kernel mode in
+    /// clock ticks.
     pub stime: u64,
 
-    /// The resident set size, or the number of pages the process has in real memory.
+    /// The resident set size, or the number of pages the process has in real
+    /// memory.
     pub rss: u64,
 
     /// The start time of the process, represented in clock ticks.
@@ -56,8 +60,8 @@ pub(crate) struct Stat {
 impl Stat {
     #[inline]
     fn from_file(mut f: File, buffer: &mut String) -> anyhow::Result<Stat> {
-        // Since this is just one line, we can read it all at once. However, since it might have non-utf8 characters,
-        // we can't just use read_to_string.
+        // Since this is just one line, we can read it all at once. However, since it
+        // might have non-utf8 characters, we can't just use read_to_string.
         f.read_to_end(unsafe { buffer.as_mut_vec() })?;
 
         let line = buffer.to_string_lossy();
@@ -82,12 +86,14 @@ impl Stat {
             .ok_or_else(|| anyhow!("missing state"))?;
         let ppid: Pid = next_part(&mut rest)?.parse()?;
 
-        // Skip 9 fields until utime (pgrp, session, tty_nr, tpgid, flags, minflt, cminflt, majflt, cmajflt).
+        // Skip 9 fields until utime (pgrp, session, tty_nr, tpgid, flags, minflt,
+        // cminflt, majflt, cmajflt).
         let mut rest = rest.skip(9);
         let utime: u64 = next_part(&mut rest)?.parse()?;
         let stime: u64 = next_part(&mut rest)?.parse()?;
 
-        // Skip 6 fields until starttime (cutime, cstime, priority, nice, num_threads, itrealvalue).
+        // Skip 6 fields until starttime (cutime, cstime, priority, nice, num_threads,
+        // itrealvalue).
         let mut rest = rest.skip(6);
         let start_time: u64 = next_part(&mut rest)?.parse()?;
 
@@ -115,7 +121,8 @@ impl Stat {
 
 /// A wrapper around the data in `/proc/<PID>/io`.
 ///
-/// Note this does not necessarily get all fields, only the ones we use in bottom.
+/// Note this does not necessarily get all fields, only the ones we use in
+/// bottom.
 pub(crate) struct Io {
     pub read_bytes: u64,
     pub write_bytes: u64,
@@ -136,7 +143,8 @@ impl Io {
         let mut read_bytes = 0;
         let mut write_bytes = 0;
 
-        // This saves us from doing a string allocation on each iteration compared to `lines()`.
+        // This saves us from doing a string allocation on each iteration compared to
+        // `lines()`.
         while let Ok(bytes) = reader.read_line(buffer) {
             if bytes > 0 {
                 if buffer.is_empty() {
@@ -207,12 +215,13 @@ fn reset(root: &mut PathBuf, buffer: &mut String) {
 }
 
 impl Process {
-    /// Creates a new [`Process`] given a `/proc/<PID>` path. This may fail if the process
-    /// no longer exists or there are permissions issues.
+    /// Creates a new [`Process`] given a `/proc/<PID>` path. This may fail if
+    /// the process no longer exists or there are permissions issues.
     ///
-    /// Note that this pre-allocates fields on **creation**! As such, some data might end
-    /// up "outdated" depending on when you call some of the methods. Therefore, this struct
-    /// is only useful for either fields that are unlikely to change, or are short-lived and
+    /// Note that this pre-allocates fields on **creation**! As such, some data
+    /// might end up "outdated" depending on when you call some of the
+    /// methods. Therefore, this struct is only useful for either fields
+    /// that are unlikely to change, or are short-lived and
     /// will be discarded quickly.
     pub(crate) fn from_path(pid_path: PathBuf) -> anyhow::Result<Process> {
         // TODO: Pass in a buffer vec/string to share?
@@ -247,7 +256,8 @@ impl Process {
         let mut root = pid_path;
         let mut buffer = String::new();
 
-        // NB: Whenever you add a new stat, make sure to pop the root and clear the buffer!
+        // NB: Whenever you add a new stat, make sure to pop the root and clear the
+        // buffer!
         let stat =
             open_at(&mut root, "stat", &fd).and_then(|file| Stat::from_file(file, &mut buffer))?;
         reset(&mut root, &mut buffer);
@@ -286,8 +296,9 @@ fn cmdline(root: &mut PathBuf, fd: &OwnedFd, buffer: &mut String) -> anyhow::Res
         .map_err(Into::into)
 }
 
-/// Opens a path. Note that this function takes in a mutable root - this will mutate it to avoid allocations. You
-/// probably will want to pop the most recent child after if you need to use the buffer again.
+/// Opens a path. Note that this function takes in a mutable root - this will
+/// mutate it to avoid allocations. You probably will want to pop the most
+/// recent child after if you need to use the buffer again.
 #[inline]
 fn open_at(root: &mut PathBuf, child: &str, fd: &OwnedFd) -> anyhow::Result<File> {
     root.push(child);
diff --git a/src/data_collection/processes/macos.rs b/src/data_collection/processes/macos.rs
index e04b6ae8..46f3273a 100644
--- a/src/data_collection/processes/macos.rs
+++ b/src/data_collection/processes/macos.rs
@@ -22,7 +22,8 @@ impl UnixProcessExt for MacOSProcessExt {
         let output = Command::new("ps")
             .args(["-o", "pid=,pcpu=", "-p"])
             .arg(
-                // Has to look like this since otherwise, it you hit a `unstable_name_collisions` warning.
+                // Has to look like this since otherwise, it you hit a `unstable_name_collisions`
+                // warning.
                 Itertools::intersperse(pids.iter().map(i32::to_string), ",".to_string())
                     .collect::<String>(),
             )
diff --git a/src/data_collection/processes/macos/sysctl_bindings.rs b/src/data_collection/processes/macos/sysctl_bindings.rs
index f9ff358a..53a7f3de 100644
--- a/src/data_collection/processes/macos/sysctl_bindings.rs
+++ b/src/data_collection/processes/macos/sysctl_bindings.rs
@@ -1,5 +1,5 @@
-//! Partial bindings from Apple's open source code for getting process information.
-//! Some of this is based on [heim's binding implementation](https://github.com/heim-rs/heim/blob/master/heim-process/src/sys/macos/bindings/process.rs).
+//! Partial bindings from Apple's open source code for getting process
+//! information. Some of this is based on [heim's binding implementation](https://github.com/heim-rs/heim/blob/master/heim-process/src/sys/macos/bindings/process.rs).
 
 use std::mem;
 
@@ -152,7 +152,8 @@ pub(crate) struct extern_proc {
     /// Pointer to process group. Originally a pointer to a `pgrp`.
     pub p_pgrp: *mut c_void,
 
-    /// Kernel virtual addr of u-area (PROC ONLY). Originally a pointer to a `user`.
+    /// Kernel virtual addr of u-area (PROC ONLY). Originally a pointer to a
+    /// `user`.
     pub p_addr: *mut c_void,
 
     /// Exit status for wait; also stop signal.
@@ -207,10 +208,12 @@ pub(crate) struct vmspace {
 #[allow(non_camel_case_types)]
 #[repr(C)]
 pub(crate) struct eproc {
-    /// Address of proc. We just cheat and use a c_void pointer since we aren't using this.
+    /// Address of proc. We just cheat and use a c_void pointer since we aren't
+    /// using this.
     pub e_paddr: *mut c_void,
 
-    /// Session pointer.  We just cheat and use a c_void pointer since we aren't using this.
+    /// Session pointer.  We just cheat and use a c_void pointer since we aren't
+    /// using this.
     pub e_sess: *mut c_void,
 
     /// Process credentials
@@ -237,7 +240,8 @@ pub(crate) struct eproc {
     /// tty process group id
     pub e_tpgid: pid_t,
 
-    /// tty session pointer.  We just cheat and use a c_void pointer since we aren't using this.
+    /// tty session pointer.  We just cheat and use a c_void pointer since we
+    /// aren't using this.
     pub e_tsess: *mut c_void,
 
     /// wchan message
@@ -291,8 +295,8 @@ pub(crate) fn kinfo_process(pid: Pid) -> Result<kinfo_proc> {
         bail!("failed to get process for pid {pid}");
     }
 
-    // SAFETY: info is initialized if result succeeded and returned a non-negative result. If sysctl failed, it returns
-    // -1 with errno set.
+    // SAFETY: info is initialized if result succeeded and returned a non-negative
+    // result. If sysctl failed, it returns -1 with errno set.
     //
     // Source: https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/sysctl.3.html
     unsafe { Ok(info.assume_init()) }
diff --git a/src/data_collection/processes/unix/process_ext.rs b/src/data_collection/processes/unix/process_ext.rs
index 07077ee2..eeef07ce 100644
--- a/src/data_collection/processes/unix/process_ext.rs
+++ b/src/data_collection/processes/unix/process_ext.rs
@@ -6,7 +6,10 @@ use hashbrown::HashMap;
 use sysinfo::{ProcessStatus, System};
 
 use super::ProcessHarvest;
-use crate::{data_collection::processes::UserTable, data_collection::Pid, utils::error};
+use crate::{
+    data_collection::{processes::UserTable, Pid},
+    utils::error,
+};
 
 pub(crate) trait UnixProcessExt {
     fn sysinfo_process_data(
diff --git a/src/data_collection/processes/unix/user_table.rs b/src/data_collection/processes/unix/user_table.rs
index 6245a858..b36008ea 100644
--- a/src/data_collection/processes/unix/user_table.rs
+++ b/src/data_collection/processes/unix/user_table.rs
@@ -12,7 +12,8 @@ impl UserTable {
         if let Some(user) = self.uid_user_mapping.get(&uid) {
             Ok(user.clone())
         } else {
-            // SAFETY: getpwuid returns a null pointer if no passwd entry is found for the uid
+            // SAFETY: getpwuid returns a null pointer if no passwd entry is found for the
+            // uid
             let passwd = unsafe { libc::getpwuid(uid) };
 
             if passwd.is_null() {
diff --git a/src/data_collection/processes/windows.rs b/src/data_collection/processes/windows.rs
index d8e3fea9..8d59f515 100644
--- a/src/data_collection/processes/windows.rs
+++ b/src/data_collection/processes/windows.rs
@@ -103,8 +103,9 @@ pub fn sysinfo_process_data(
                 .and_then(|uid| users.get_user_by_id(uid))
                 .map_or_else(|| "N/A".into(), |user| user.name().to_owned().into()),
             time: if process_val.start_time() == 0 {
-                // Workaround for Windows occasionally returning a start time equal to UNIX epoch, giving a run time
-                // in the range of 50+ years. We just return a time of zero in this case for simplicity.
+                // Workaround for Windows occasionally returning a start time equal to UNIX
+                // epoch, giving a run time in the range of 50+ years. We just
+                // return a time of zero in this case for simplicity.
                 Duration::ZERO
             } else {
                 Duration::from_secs(process_val.run_time())
diff --git a/src/data_collection/temperature.rs b/src/data_collection/temperature.rs
index b85d1f81..34130ab6 100644
--- a/src/data_collection/temperature.rs
+++ b/src/data_collection/temperature.rs
@@ -47,7 +47,8 @@ impl FromStr for TemperatureType {
 }
 
 impl TemperatureType {
-    /// Given a temperature in Celsius, covert it if necessary for a different unit.
+    /// Given a temperature in Celsius, covert it if necessary for a different
+    /// unit.
     pub fn convert_temp_unit(&self, temp_celsius: f32) -> f32 {
         fn convert_celsius_to_kelvin(celsius: f32) -> f32 {
             celsius + 273.15
diff --git a/src/data_collection/temperature/linux.rs b/src/data_collection/temperature/linux.rs
index 50b858d1..ef920e6d 100644
--- a/src/data_collection/temperature/linux.rs
+++ b/src/data_collection/temperature/linux.rs
@@ -13,13 +13,15 @@ use crate::{app::filter::Filter, utils::error::BottomError};
 
 const EMPTY_NAME: &str = "Unknown";
 
-/// Returned results from grabbing hwmon/coretemp temperature sensor values/names.
+/// Returned results from grabbing hwmon/coretemp temperature sensor
+/// values/names.
 struct HwmonResults {
     temperatures: Vec<TempHarvest>,
     num_hwmon: usize,
 }
 
-/// Parses and reads temperatures that were in millidegree Celsius, and if successful, returns a temperature in Celsius.
+/// Parses and reads temperatures that were in millidegree Celsius, and if
+/// successful, returns a temperature in Celsius.
 fn parse_temp(path: &Path) -> Result<f32> {
     Ok(fs::read_to_string(path)?
         .trim_end()
@@ -28,7 +30,8 @@ fn parse_temp(path: &Path) -> Result<f32> {
         / 1_000.0)
 }
 
-/// Get all candidates from hwmon and coretemp. It will also return the number of entries from hwmon.
+/// Get all candidates from hwmon and coretemp. It will also return the number
+/// of entries from hwmon.
 fn get_hwmon_candidates() -> (HashSet<PathBuf>, usize) {
     let mut dirs = HashSet::default();
 
@@ -36,11 +39,13 @@ fn get_hwmon_candidates() -> (HashSet<PathBuf>, usize) {
         for entry in read_dir.flatten() {
             let mut path = entry.path();
 
-            // hwmon includes many sensors, we only want ones with at least one temperature sensor
-            // Reading this file will wake the device, but we're only checking existence, so it should be fine.
+            // hwmon includes many sensors, we only want ones with at least one temperature
+            // sensor Reading this file will wake the device, but we're only
+            // checking existence, so it should be fine.
             if !path.join("temp1_input").exists() {
-                // Note we also check for a `device` subdirectory (e.g. `/sys/class/hwmon/hwmon*/device/`).
-                // This is needed for CentOS, which adds this extra `/device` directory. See:
+                // Note we also check for a `device` subdirectory (e.g.
+                // `/sys/class/hwmon/hwmon*/device/`). This is needed for
+                // CentOS, which adds this extra `/device` directory. See:
                 // - https://github.com/nicolargo/glances/issues/1060
                 // - https://github.com/giampaolo/psutil/issues/971
                 // - https://github.com/giampaolo/psutil/blob/642438375e685403b4cd60b0c0e25b80dd5a813d/psutil/_pslinux.py#L1316
@@ -65,8 +70,9 @@ fn get_hwmon_candidates() -> (HashSet<PathBuf>, usize) {
                         let path = entry.path();
 
                         if path.join("temp1_input").exists() {
-                            // It's possible that there are dupes (represented by symlinks) - the easy
-                            // way is to just substitute the parent directory and check if the hwmon
+                            // It's possible that there are dupes (represented by symlinks) - the
+                            // easy way is to just substitute the parent
+                            // directory and check if the hwmon
                             // variant exists already in a set.
                             //
                             // For more info, see https://github.com/giampaolo/psutil/pull/1822/files
@@ -175,7 +181,8 @@ fn finalize_name(
 }
 
 /// Whether the temperature should *actually* be read during enumeration.
-/// Will return false if the state is not D0/unknown, or if it does not support `device/power_state`.
+/// Will return false if the state is not D0/unknown, or if it does not support
+/// `device/power_state`.
 #[inline]
 fn is_device_awake(path: &Path) -> bool {
     // Whether the temperature should *actually* be read during enumeration.
@@ -186,10 +193,12 @@ fn is_device_awake(path: &Path) -> bool {
     if power_state.exists() {
         if let Ok(state) = fs::read_to_string(power_state) {
             let state = state.trim();
-            // The zenpower3 kernel module (incorrectly?) reports "unknown", causing this check
-            // to fail and temperatures to appear as zero instead of having the file not exist.
+            // The zenpower3 kernel module (incorrectly?) reports "unknown", causing this
+            // check to fail and temperatures to appear as zero instead of
+            // having the file not exist.
             //
-            // Their self-hosted git instance has disabled sign up, so this bug cant be reported either.
+            // Their self-hosted git instance has disabled sign up, so this bug cant be
+            // reported either.
             state == "D0" || state == "unknown"
         } else {
             true
@@ -199,9 +208,10 @@ fn is_device_awake(path: &Path) -> bool {
     }
 }
 
-/// Get temperature sensors from the linux sysfs interface `/sys/class/hwmon` and
-/// `/sys/devices/platform/coretemp.*`. It returns all found temperature sensors, and the number
-/// of checked hwmon directories (not coretemp directories).
+/// Get temperature sensors from the linux sysfs interface `/sys/class/hwmon`
+/// and `/sys/devices/platform/coretemp.*`. It returns all found temperature
+/// sensors, and the number of checked hwmon directories (not coretemp
+/// directories).
 ///
 /// For more details, see the relevant Linux kernel documentation:
 /// - [`/sys/class/hwmon`](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-hwmon)
@@ -233,7 +243,8 @@ fn hwmon_temperatures(temp_type: &TemperatureType, filter: &Option<Filter>) -> H
     // will not wake the device, and thus not block,
     // and meaning no sensors have to be hidden depending on `power_state`
     //
-    // It would probably be more ideal to use a proper async runtime; this would also allow easy cancellation/timeouts.
+    // It would probably be more ideal to use a proper async runtime; this would
+    // also allow easy cancellation/timeouts.
     for file_path in dirs {
         let sensor_name = read_to_string_lossy(file_path.join("name"));
 
@@ -264,18 +275,19 @@ fn hwmon_temperatures(temp_type: &TemperatureType, filter: &Option<Filter>) -> H
 
                 // Do some messing around to get a more sensible name for sensors:
                 // - For GPUs, this will use the kernel device name, ex `card0`
-                // - For nvme drives, this will also use the kernel name, ex `nvme0`.
-                //   This is found differently than for GPUs
+                // - For nvme drives, this will also use the kernel name, ex `nvme0`. This is
+                //   found differently than for GPUs
                 // - For whatever acpitz is, on my machine this is now `thermal_zone0`.
                 // - For k10temp, this will still be k10temp, but it has to be handled special.
                 let hwmon_name = {
                     let device = file_path.join("device");
 
-                    // This will exist for GPUs but not others, this is how we find their kernel name.
+                    // This will exist for GPUs but not others, this is how we find their kernel
+                    // name.
                     let drm = device.join("drm");
                     if drm.exists() {
-                        // This should never actually be empty. If it is though, we'll fall back to the sensor name
-                        // later on.
+                        // This should never actually be empty. If it is though, we'll fall back to
+                        // the sensor name later on.
                         let mut gpu = None;
 
                         if let Ok(cards) = drm.read_dir() {
@@ -294,10 +306,11 @@ fn hwmon_temperatures(temp_type: &TemperatureType, filter: &Option<Filter>) -> H
 
                         gpu
                     } else {
-                        // This little mess is to account for stuff like k10temp. This is needed because the
-                        // `device` symlink points to `nvme*` for nvme drives, but to PCI buses for anything
-                        // else. If the first character is alphabetic, it's an actual name like k10temp or
-                        // nvme0, not a PCI bus.
+                        // This little mess is to account for stuff like k10temp. This is needed
+                        // because the `device` symlink points to `nvme*`
+                        // for nvme drives, but to PCI buses for anything
+                        // else. If the first character is alphabetic, it's an actual name like
+                        // k10temp or nvme0, not a PCI bus.
                         fs::read_link(device).ok().and_then(|link| {
                             let link = link
                                 .file_name()
@@ -316,7 +329,8 @@ fn hwmon_temperatures(temp_type: &TemperatureType, filter: &Option<Filter>) -> H
 
                 let name = finalize_name(hwmon_name, sensor_label, &sensor_name, &mut seen_names);
 
-                // TODO: It's possible we may want to move the filter check further up to avoid probing hwmon if not needed?
+                // TODO: It's possible we may want to move the filter check further up to avoid
+                // probing hwmon if not needed?
                 if is_temp_filtered(filter, &name) {
                     if let Ok(temp_celsius) = parse_temp(&temp_path) {
                         temperatures.push(TempHarvest {
@@ -335,8 +349,9 @@ fn hwmon_temperatures(temp_type: &TemperatureType, filter: &Option<Filter>) -> H
     }
 }
 
-/// Gets data from `/sys/class/thermal/thermal_zone*`. This should only be used if
-/// [`hwmon_temperatures`] doesn't return anything to avoid duplicate sensor results.
+/// Gets data from `/sys/class/thermal/thermal_zone*`. This should only be used
+/// if [`hwmon_temperatures`] doesn't return anything to avoid duplicate sensor
+/// results.
 ///
 /// See [the Linux kernel documentation](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-thermal)
 /// for more details.
diff --git a/src/data_collection/temperature/sysinfo.rs b/src/data_collection/temperature/sysinfo.rs
index c6feb0b8..9e6e797e 100644
--- a/src/data_collection/temperature/sysinfo.rs
+++ b/src/data_collection/temperature/sysinfo.rs
@@ -21,7 +21,8 @@ pub fn get_temperature_data(
         }
     }
 
-    // For RockPro64 boards on FreeBSD, they apparently use "hw.temperature" for sensors.
+    // For RockPro64 boards on FreeBSD, they apparently use "hw.temperature" for
+    // sensors.
     #[cfg(target_os = "freebsd")]
     {
         use sysctl::Sysctl;
diff --git a/src/data_conversion.rs b/src/data_conversion.rs
index e08503ec..d72fae24 100644
--- a/src/data_conversion.rs
+++ b/src/data_conversion.rs
@@ -74,7 +74,8 @@ pub struct ConvertedData {
     pub cache_labels: Option<(String, String)>,
     pub swap_labels: Option<(String, String)>,
 
-    pub mem_data: Vec<Point>, /* TODO: Switch this and all data points over to a better data structure... */
+    pub mem_data: Vec<Point>, /* TODO: Switch this and all data points over to a better data
+                               * structure... */
     #[cfg(not(target_os = "windows"))]
     pub cache_data: Vec<Point>,
     pub swap_data: Vec<Point>,
@@ -169,7 +170,8 @@ impl ConvertedData {
                             data,
                             last_entry,
                         } => {
-                            // A bit faster to just update all the times, so we just clear the vector.
+                            // A bit faster to just update all the times, so we just clear the
+                            // vector.
                             data.clear();
                             *last_entry = *cpu_usage;
                         }
@@ -177,8 +179,8 @@ impl ConvertedData {
             }
         }
 
-        // TODO: [Opt] Can probably avoid data deduplication - store the shift + data + original once.
-        // Now push all the data.
+        // TODO: [Opt] Can probably avoid data deduplication - store the shift + data +
+        // original once. Now push all the data.
         for (itx, mut cpu) in &mut self.cpu_data.iter_mut().skip(1).enumerate() {
             match &mut cpu {
                 CpuWidgetData::All => unreachable!(),
@@ -262,10 +264,12 @@ pub fn convert_swap_data_points(data: &DataCollection) -> Vec<Point> {
     result
 }
 
-/// Returns the most appropriate binary prefix unit type (e.g. kibibyte) and denominator for the given amount of bytes.
+/// Returns the most appropriate binary prefix unit type (e.g. kibibyte) and
+/// denominator for the given amount of bytes.
 ///
-/// The expected usage is to divide out the given value with the returned denominator in order to be able to use it
-/// with the returned binary unit (e.g. divide 3000 bytes by 1024 to have a value in KiB).
+/// The expected usage is to divide out the given value with the returned
+/// denominator in order to be able to use it with the returned binary unit
+/// (e.g. divide 3000 bytes by 1024 to have a value in KiB).
 #[inline]
 fn get_binary_unit_and_denominator(bytes: u64) -> (&'static str, f64) {
     match bytes {
@@ -277,7 +281,8 @@ fn get_binary_unit_and_denominator(bytes: u64) -> (&'static str, f64) {
     }
 }
 
-/// Returns the unit type and denominator for given total amount of memory in kibibytes.
+/// Returns the unit type and denominator for given total amount of memory in
+/// kibibytes.
 pub fn convert_mem_label(harvest: &MemHarvest) -> Option<(String, String)> {
     if harvest.total_bytes > 0 {
         Some((format!("{:3.0}%", harvest.use_percent.unwrap_or(0.0)), {
@@ -371,7 +376,9 @@ pub fn convert_network_points(
     let (rx_converted_result, total_rx_converted_result): ((f64, String), (f64, &'static str)) =
         if use_binary_prefix {
             (
-                get_binary_prefix(rx_data, unit), /* If this isn't obvious why there's two functions, one you can configure the unit, the other is always bytes */
+                get_binary_prefix(rx_data, unit), /* If this isn't obvious why there's two
+                                                   * functions, one you can configure the unit,
+                                                   * the other is always bytes */
                 get_binary_bytes(total_rx_data),
             )
         } else {
@@ -464,8 +471,9 @@ pub fn convert_network_points(
     }
 }
 
-/// 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 decimal place.
+/// 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
+/// decimal place.
 pub fn binary_byte_string(value: u64) -> String {
     let converted_values = get_binary_bytes(value);
     if value >= GIBI_LIMIT {
@@ -486,8 +494,9 @@ pub fn dec_bytes_per_string(value: u64) -> String {
     }
 }
 
-/// 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 decimal place.
+/// 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
+/// decimal place.
 pub fn dec_bytes_per_second_string(value: u64) -> String {
     let converted_values = get_decimal_bytes(value);
     if value >= GIGA_LIMIT {
diff --git a/src/main.rs b/src/main.rs
index ca4656e1..d22e62a7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,9 +1,11 @@
-//! A customizable cross-platform graphical process/system monitor for the terminal.
-//! Supports Linux, macOS, and Windows. Inspired by gtop, gotop, and htop.
+//! A customizable cross-platform graphical process/system monitor for the
+//! terminal. Supports Linux, macOS, and Windows. Inspired by gtop, gotop, and
+//! htop.
 //!
-//! **Note:** The following documentation is primarily intended for people to refer to for development purposes rather
-//! than the actual usage of the application. If you are instead looking for documentation regarding the *usage* of
-//! bottom, refer to [here](https://clementtsang.github.io/bottom/stable/).
+//! **Note:** The following documentation is primarily intended for people to
+//! refer to for development purposes rather than the actual usage of the
+//! application. If you are instead looking for documentation regarding the
+//! *usage* of bottom, refer to [here](https://clementtsang.github.io/bottom/stable/).
 
 pub mod app;
 pub mod utils {
@@ -48,8 +50,7 @@ use crossterm::{
 };
 use data_conversion::*;
 use event::{handle_key_event_or_break, handle_mouse_event, BottomEvent, CollectionThreadEvent};
-use options::args;
-use options::{get_color_scheme, get_config_path, get_or_create_config, init_app};
+use options::{args, get_color_scheme, get_config_path, get_or_create_config, init_app};
 use tui::{backend::CrosstermBackend, Terminal};
 use utils::error;
 #[allow(unused_imports)]
@@ -156,9 +157,11 @@ fn create_input_thread(
                     if let Ok(event) = read() {
                         match event {
                             Event::Resize(_, _) => {
-                                // TODO: Might want to debounce this in the future, or take into account the actual resize
-                                // values. Maybe we want to keep the current implementation in case the resize event might
-                                // not fire... not sure.
+                                // TODO: Might want to debounce this in the future, or take into
+                                // account the actual resize values.
+                                // Maybe we want to keep the current implementation in case the
+                                // resize event might not fire...
+                                // not sure.
 
                                 if sender.send(BottomEvent::Resize).is_err() {
                                     break;
@@ -170,7 +173,8 @@ fn create_input_thread(
                                 }
                             }
                             Event::Key(key) if key.kind == KeyEventKind::Press => {
-                                // For now, we only care about key down events. This may change in the future.
+                                // For now, we only care about key down events. This may change in
+                                // the future.
                                 if sender.send(BottomEvent::KeyInput(key)).is_err() {
                                     break;
                                 }
@@ -302,7 +306,8 @@ fn main() -> anyhow::Result<()> {
         CanvasStyling::new(colour_scheme, &config)?
     };
 
-    // Create an "app" struct, which will control most of the program and store settings/state
+    // Create an "app" struct, which will control most of the program and store
+    // settings/state
     let (mut app, widget_layout) = init_app(args, config, &styling)?;
 
     // Create painter and set colours.
@@ -311,14 +316,16 @@ fn main() -> anyhow::Result<()> {
     // Check if the current environment is in a terminal.
     check_if_terminal();
 
-    // Create termination mutex and cvar. We use this setup because we need to sleep at some points in the update
-    // thread, but we want to be able to interrupt the "sleep" if a termination occurs.
+    // Create termination mutex and cvar. We use this setup because we need to sleep
+    // at some points in the update thread, but we want to be able to interrupt
+    // the "sleep" if a termination occurs.
     let termination_lock = Arc::new(Mutex::new(false));
     let termination_cvar = Arc::new(Condvar::new());
 
     let (sender, receiver) = mpsc::channel();
 
-    // Set up the event loop thread; we set this up early to speed up first-time-to-data.
+    // Set up the event loop thread; we set this up early to speed up
+    // first-time-to-data.
     let (collection_thread_ctrl_sender, collection_thread_ctrl_receiver) = mpsc::channel();
     let _collection_thread = create_collection_thread(
         sender.clone(),
@@ -394,7 +401,8 @@ fn main() -> anyhow::Result<()> {
 
     let mut first_run = true;
 
-    // Draw once first to initialize the canvas, so it doesn't feel like it's frozen.
+    // Draw once first to initialize the canvas, so it doesn't feel like it's
+    // frozen.
     try_drawing(&mut terminal, &mut app, &mut painter)?;
 
     loop {
diff --git a/src/options.rs b/src/options.rs
index 998cc285..345981bd 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -16,7 +16,7 @@ use std::{
 };
 
 use anyhow::{Context, Result};
-pub use colours::ConfigColours;
+pub use colours::ColoursConfig;
 pub use config::ConfigV1;
 use hashbrown::{HashMap, HashSet};
 use indexmap::IndexSet;
@@ -62,9 +62,10 @@ macro_rules! is_flag_enabled {
     };
 }
 
-/// Returns the config path to use. If `override_config_path` is specified, then we will use
-/// that. If not, then return the "default" config path, which is:
-/// - If a path already exists at `<HOME>/bottom/bottom.toml`, then use that for legacy reasons.
+/// Returns the config path to use. If `override_config_path` is specified, then
+/// we will use that. If not, then return the "default" config path, which is:
+/// - If a path already exists at `<HOME>/bottom/bottom.toml`, then use that for
+///   legacy reasons.
 /// - Otherwise, use `<SYSTEM_CONFIG_FOLDER>/bottom/bottom.toml`.
 ///
 /// For more details on this, see [dirs](https://docs.rs/dirs/latest/dirs/fn.config_dir.html)'
@@ -93,9 +94,10 @@ pub fn get_config_path(override_config_path: Option<&Path>) -> Option<PathBuf> {
     })
 }
 
-/// Get the config at `config_path`. If there is no config file at the specified path, it will
-/// try to create a new file with the default settings, and return the default config. If bottom
-/// fails to write a new config, it will silently just return the default config.
+/// Get the config at `config_path`. If there is no config file at the specified
+/// path, it will try to create a new file with the default settings, and return
+/// the default config. If bottom fails to write a new config, it will silently
+/// just return the default config.
 pub fn get_or_create_config(config_path: Option<&Path>) -> error::Result<ConfigV1> {
     match &config_path {
         Some(path) => {
@@ -111,7 +113,8 @@ pub fn get_or_create_config(config_path: Option<&Path>) -> error::Result<ConfigV
             }
         }
         None => {
-            // If we somehow don't have any config path, then just assume the default config but don't write to any file.
+            // If we somehow don't have any config path, then just assume the default config
+            // but don't write to any file.
             //
             // TODO: Maybe make this "show" an error, but don't crash.
             Ok(ConfigV1::default())
@@ -124,7 +127,8 @@ pub fn init_app(
 ) -> Result<(App, BottomLayout)> {
     use BottomWidgetType::*;
 
-    // Since everything takes a reference, but we want to take ownership here to drop matches/config later...
+    // Since everything takes a reference, but we want to take ownership here to
+    // drop matches/config later...
     let args = &args;
     let config = &config;
 
@@ -175,18 +179,13 @@ pub fn init_app(
         is_flag_enabled!(network_use_binary_prefix, args.network, config);
 
     let proc_columns: Option<IndexSet<ProcWidgetColumn>> = {
-        let columns = config.processes.as_ref().map(|cfg| cfg.columns.clone());
-
-        match columns {
-            Some(columns) => {
-                if columns.is_empty() {
-                    None
-                } else {
-                    Some(IndexSet::from_iter(columns))
-                }
+        config.processes.as_ref().and_then(|cfg| {
+            if cfg.columns.is_empty() {
+                None
+            } else {
+                Some(IndexSet::from_iter(cfg.columns.clone()))
             }
-            None => None,
-        }
+        })
     };
 
     let network_legend_position = get_network_legend_position(args, config)?;
@@ -384,14 +383,29 @@ pub fn init_app(
         use_battery: used_widget_set.get(&Battery).is_some(),
     };
 
-    let disk_filter =
-        get_ignore_list(&config.disk_filter).context("Update 'disk_filter' in your config file")?;
-    let mount_filter = get_ignore_list(&config.mount_filter)
-        .context("Update 'mount_filter' in your config file")?;
-    let temp_filter =
-        get_ignore_list(&config.temp_filter).context("Update 'temp_filter' in your config file")?;
-    let net_filter =
-        get_ignore_list(&config.net_filter).context("Update 'net_filter' in your config file")?;
+    let (disk_name_filter, disk_mount_filter) = {
+        match &config.disk {
+            Some(cfg) => {
+                let df = get_ignore_list(&cfg.name_filter)
+                    .context("Update 'disk.name_filter' in your config file")?;
+                let mf = get_ignore_list(&cfg.mount_filter)
+                    .context("Update 'disk.mount_filter' in your config file")?;
+
+                (df, mf)
+            }
+            None => (None, None),
+        }
+    };
+    let temp_sensor_filter = match &config.temperature {
+        Some(cfg) => get_ignore_list(&cfg.sensor_filter)
+            .context("Update 'temperature.sensor_filter' in your config file")?,
+        None => None,
+    };
+    let net_interface_filter = match &config.network {
+        Some(cfg) => get_ignore_list(&cfg.interface_filter)
+            .context("Update 'network.interface_filter' in your config file")?,
+        None => None,
+    };
 
     let states = AppWidgetStates {
         cpu_state: CpuState::init(cpu_state_map),
@@ -406,10 +420,10 @@ pub fn init_app(
 
     let current_widget = widget_map.get(&initial_widget_id).unwrap().clone();
     let filters = DataFilters {
-        disk_filter,
-        mount_filter,
-        temp_filter,
-        net_filter,
+        disk_filter: disk_name_filter,
+        mount_filter: disk_mount_filter,
+        temp_filter: temp_sensor_filter,
+        net_filter: net_interface_filter,
     };
     let is_expanded = expanded && !use_basic_mode;
 
@@ -686,7 +700,8 @@ fn get_default_widget_and_count(
 fn get_use_battery(args: &BottomArgs, config: &ConfigV1) -> bool {
     #[cfg(feature = "battery")]
     {
-        // TODO: Move this so it's dynamic in the app itself and automatically hide if there are no batteries?
+        // TODO: Move this so it's dynamic in the app itself and automatically hide if
+        // there are no batteries?
         if let Ok(battery_manager) = Manager::new() {
             if let Ok(batteries) = battery_manager.batteries() {
                 if batteries.count() == 0 {
@@ -907,7 +922,7 @@ mod test {
         args::BottomArgs,
         canvas::styling::CanvasStyling,
         options::{
-            config::ConfigFlags, get_default_time_value, get_retention, get_update_rate,
+            config::FlagConfig, get_default_time_value, get_retention, get_update_rate,
             try_parse_ms,
         },
     };
@@ -986,7 +1001,7 @@ mod test {
         let args = BottomArgs::parse_from(["btm"]);
 
         let mut config = ConfigV1::default();
-        let flags = ConfigFlags {
+        let flags = FlagConfig {
             time_delta: Some("2 min".to_string().into()),
             default_time_value: Some("300s".to_string().into()),
             rate: Some("1s".to_string().into()),
@@ -1016,7 +1031,7 @@ mod test {
         let args = BottomArgs::parse_from(["btm"]);
 
         let mut config = ConfigV1::default();
-        let flags = ConfigFlags {
+        let flags = FlagConfig {
             time_delta: Some("120000".to_string().into()),
             default_time_value: Some("300000".to_string().into()),
             rate: Some("1000".to_string().into()),
@@ -1046,7 +1061,7 @@ mod test {
         let args = BottomArgs::parse_from(["btm"]);
 
         let mut config = ConfigV1::default();
-        let flags = ConfigFlags {
+        let flags = FlagConfig {
             time_delta: Some(120000.into()),
             default_time_value: Some(300000.into()),
             rate: Some(1000.into()),
@@ -1079,15 +1094,17 @@ mod test {
         super::init_app(args, config, &styling).unwrap().0
     }
 
-    // TODO: There's probably a better way to create clap options AND unify together to avoid the possibility of
-    // typos/mixing up. Use proc macros to unify on one struct?
+    // TODO: There's probably a better way to create clap options AND unify together
+    // to avoid the possibility of typos/mixing up. Use proc macros to unify on
+    // one struct?
     #[test]
     fn verify_cli_options_build() {
         let app = crate::args::build_cmd();
 
         let default_app = create_app(BottomArgs::parse_from(["btm"]));
 
-        // Skip battery since it's tricky to test depending on the platform/features we're testing with.
+        // Skip battery since it's tricky to test depending on the platform/features
+        // we're testing with.
         let skip = ["help", "version", "celsius", "battery"];
 
         for arg in app.get_arguments().collect::<Vec<_>>() {
diff --git a/src/options/args.rs b/src/options/args.rs
index 041ac7ec..27c773d7 100644
--- a/src/options/args.rs
+++ b/src/options/args.rs
@@ -1,7 +1,7 @@
 //! Argument parsing via clap.
 //!
-//! Note that you probably want to keep this as a single file so the build script doesn't
-//! trip all over itself.
+//! Note that you probably want to keep this as a single file so the build
+//! script doesn't trip all over itself.
 
 // TODO: New sections are misaligned! See if we can get that fixed.
 
@@ -557,7 +557,8 @@ pub struct StyleArgs {
     pub color: Option<String>,
 }
 
-/// Other arguments. This just handle options that are for help/version displaying.
+/// Other arguments. This just handle options that are for help/version
+/// displaying.
 #[derive(Args, Clone, Debug)]
 #[command(next_help_heading = "Other Options", rename_all = "snake_case")]
 pub struct OtherArgs {
diff --git a/src/options/colours.rs b/src/options/colours.rs
index edef00b4..e01e2ed0 100644
--- a/src/options/colours.rs
+++ b/src/options/colours.rs
@@ -3,7 +3,7 @@ use std::borrow::Cow;
 use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub struct ConfigColours {
+pub struct ColoursConfig {
     pub table_header_color: Option<Cow<'static, str>>,
     pub all_cpu_color: Option<Cow<'static, str>>,
     pub avg_cpu_color: Option<Cow<'static, str>>,
@@ -31,8 +31,9 @@ pub struct ConfigColours {
     pub low_battery_color: Option<Cow<'static, str>>,
 }
 
-impl ConfigColours {
-    /// Returns `true` if there is a [`ConfigColours`] that is empty or there isn't one at all.
+impl ColoursConfig {
+    /// Returns `true` if there is a [`ConfigColours`] that is empty or there
+    /// isn't one at all.
     pub fn is_empty(&self) -> bool {
         if let Ok(serialized_string) = toml_edit::ser::to_string(self) {
             return serialized_string.is_empty();
diff --git a/src/options/config.rs b/src/options/config.rs
index 975472f1..659563bf 100644
--- a/src/options/config.rs
+++ b/src/options/config.rs
@@ -1,24 +1,29 @@
 pub mod cpu;
+pub mod disk;
 mod ignore_list;
 pub mod layout;
-pub mod process_columns;
+pub mod network;
+pub mod process;
+pub mod temperature;
 
+use disk::DiskConfig;
+use network::NetworkConfig;
 use serde::{Deserialize, Serialize};
+use temperature::TempConfig;
 
 pub use self::ignore_list::IgnoreList;
-use self::{cpu::CpuConfig, layout::Row, process_columns::ProcessConfig};
-use super::ConfigColours;
+use self::{cpu::CpuConfig, layout::Row, process::ProcessesConfig};
+use super::ColoursConfig;
 
 #[derive(Clone, Debug, Default, Deserialize)]
 pub struct ConfigV1 {
-    pub(crate) flags: Option<ConfigFlags>,
-    pub(crate) colors: Option<ConfigColours>,
+    pub(crate) flags: Option<FlagConfig>,
+    pub(crate) colors: Option<ColoursConfig>,
     pub(crate) row: Option<Vec<Row>>,
-    pub(crate) disk_filter: Option<IgnoreList>,
-    pub(crate) mount_filter: Option<IgnoreList>,
-    pub(crate) temp_filter: Option<IgnoreList>,
-    pub(crate) net_filter: Option<IgnoreList>,
-    pub(crate) processes: Option<ProcessConfig>,
+    pub(crate) processes: Option<ProcessesConfig>,
+    pub(crate) disk: Option<DiskConfig>,
+    pub(crate) temperature: Option<TempConfig>,
+    pub(crate) network: Option<NetworkConfig>,
     pub(crate) cpu: Option<CpuConfig>,
 }
 
@@ -42,7 +47,7 @@ impl From<u64> for StringOrNum {
 }
 
 #[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub(crate) struct ConfigFlags {
+pub(crate) struct FlagConfig {
     pub(crate) hide_avg_cpu: Option<bool>,
     pub(crate) dot_marker: Option<bool>,
     pub(crate) temperature_type: Option<String>,
diff --git a/src/options/config/cpu.rs b/src/options/config/cpu.rs
index ce027305..c5af891d 100644
--- a/src/options/config/cpu.rs
+++ b/src/options/config/cpu.rs
@@ -1,6 +1,7 @@
 use serde::Deserialize;
 
-/// The default selection of the CPU widget. If the given selection is invalid, we will fall back to all.
+/// The default selection of the CPU widget. If the given selection is invalid,
+/// we will fall back to all.
 #[derive(Clone, Copy, Debug, Default, Deserialize)]
 #[serde(rename_all = "lowercase")]
 pub enum CpuDefault {
diff --git a/src/options/config/disk.rs b/src/options/config/disk.rs
new file mode 100644
index 00000000..355416bc
--- /dev/null
+++ b/src/options/config/disk.rs
@@ -0,0 +1,13 @@
+use serde::Deserialize;
+
+use super::IgnoreList;
+
+/// Disk configuration.
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct DiskConfig {
+    /// A filter over the disk names.
+    pub name_filter: Option<IgnoreList>,
+
+    /// A filter over the mount names.
+    pub mount_filter: Option<IgnoreList>,
+}
diff --git a/src/options/config/ignore_list.rs b/src/options/config/ignore_list.rs
index 472f7fa6..f0d5325b 100644
--- a/src/options/config/ignore_list.rs
+++ b/src/options/config/ignore_list.rs
@@ -9,7 +9,8 @@ fn default_as_true() -> bool {
 pub struct IgnoreList {
     #[serde(default = "default_as_true")]
     // TODO: Deprecate and/or rename, current name sounds awful.
-    // Maybe to something like "deny_entries"?  Currently it defaults to a denylist anyways, so maybe "allow_entries"?
+    // Maybe to something like "deny_entries"?  Currently it defaults to a denylist anyways, so
+    // maybe "allow_entries"?
     pub is_list_ignored: bool,
     pub list: Vec<String>,
     #[serde(default)]
diff --git a/src/options/config/network.rs b/src/options/config/network.rs
new file mode 100644
index 00000000..7e75fdd4
--- /dev/null
+++ b/src/options/config/network.rs
@@ -0,0 +1,10 @@
+use serde::Deserialize;
+
+use super::IgnoreList;
+
+/// Network configuration.
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct NetworkConfig {
+    /// A filter over the network interface names.
+    pub interface_filter: Option<IgnoreList>,
+}
diff --git a/src/options/config/process_columns.rs b/src/options/config/process.rs
similarity index 72%
rename from src/options/config/process_columns.rs
rename to src/options/config/process.rs
index ea65bf27..f8bbe866 100644
--- a/src/options/config/process_columns.rs
+++ b/src/options/config/process.rs
@@ -2,22 +2,23 @@ use serde::Deserialize;
 
 use crate::widgets::ProcWidgetColumn;
 
-/// Process column settings.
+/// Process configuration.
 #[derive(Clone, Debug, Default, Deserialize)]
-pub struct ProcessConfig {
+pub struct ProcessesConfig {
+    /// A list of process widget columns.
     #[serde(default)]
     pub columns: Vec<ProcWidgetColumn>,
 }
 
 #[cfg(test)]
 mod test {
-    use super::ProcessConfig;
+    use super::ProcessesConfig;
     use crate::widgets::ProcWidgetColumn;
 
     #[test]
     fn empty_column_setting() {
         let config = "";
-        let generated: ProcessConfig = toml_edit::de::from_str(config).unwrap();
+        let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
         assert!(generated.columns.is_empty());
     }
 
@@ -27,7 +28,7 @@ mod test {
             columns = ["CPU%", "PiD", "user", "MEM", "Tread", "T.Write", "Rps", "W/s", "tiMe", "USER", "state"]
         "#;
 
-        let generated: ProcessConfig = toml_edit::de::from_str(config).unwrap();
+        let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
         assert_eq!(
             generated.columns,
             vec![
@@ -49,25 +50,25 @@ mod test {
     #[test]
     fn process_column_settings_2() {
         let config = r#"columns = ["MEM", "TWrite", "Cpuz", "read", "wps"]"#;
-        toml_edit::de::from_str::<ProcessConfig>(config).expect_err("Should error out!");
+        toml_edit::de::from_str::<ProcessesConfig>(config).expect_err("Should error out!");
     }
 
     #[test]
     fn process_column_settings_3() {
         let config = r#"columns = ["Twrite", "T.Write"]"#;
-        let generated: ProcessConfig = toml_edit::de::from_str(config).unwrap();
+        let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
         assert_eq!(generated.columns, vec![ProcWidgetColumn::TotalWrite; 2]);
 
         let config = r#"columns = ["Tread", "T.read"]"#;
-        let generated: ProcessConfig = toml_edit::de::from_str(config).unwrap();
+        let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
         assert_eq!(generated.columns, vec![ProcWidgetColumn::TotalRead; 2]);
 
         let config = r#"columns = ["read", "rps", "r/s"]"#;
-        let generated: ProcessConfig = toml_edit::de::from_str(config).unwrap();
+        let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
         assert_eq!(generated.columns, vec![ProcWidgetColumn::ReadPerSecond; 3]);
 
         let config = r#"columns = ["write", "wps", "w/s"]"#;
-        let generated: ProcessConfig = toml_edit::de::from_str(config).unwrap();
+        let generated: ProcessesConfig = toml_edit::de::from_str(config).unwrap();
         assert_eq!(generated.columns, vec![ProcWidgetColumn::WritePerSecond; 3]);
     }
 }
diff --git a/src/options/config/temperature.rs b/src/options/config/temperature.rs
new file mode 100644
index 00000000..266d7406
--- /dev/null
+++ b/src/options/config/temperature.rs
@@ -0,0 +1,10 @@
+use serde::Deserialize;
+
+use super::IgnoreList;
+
+/// Temperature configuration.
+#[derive(Clone, Debug, Default, Deserialize)]
+pub struct TempConfig {
+    /// A filter over the sensor names.
+    pub sensor_filter: Option<IgnoreList>,
+}
diff --git a/src/utils/data_prefixes.rs b/src/utils/data_prefixes.rs
index 516a66ba..fb2892c4 100644
--- a/src/utils/data_prefixes.rs
+++ b/src/utils/data_prefixes.rs
@@ -38,9 +38,9 @@ pub const LOG_MEBI_LIMIT_U32: u32 = 20;
 pub const LOG_GIBI_LIMIT_U32: u32 = 30;
 pub const LOG_TEBI_LIMIT_U32: u32 = 40;
 
-/// Returns a tuple containing the value and the unit in bytes.  In units of 1024.
-/// This only supports up to a tebi.  Note the "single" unit will have a space appended to match the others if
-/// `spacing` is true.
+/// Returns a tuple containing the value and the unit in bytes.  In units of
+/// 1024. This only supports up to a tebi.  Note the "single" unit will have a
+/// space appended to match the others if `spacing` is true.
 #[inline]
 pub fn get_binary_bytes(bytes: u64) -> (f64, &'static str) {
     match bytes {
@@ -52,9 +52,9 @@ pub fn get_binary_bytes(bytes: u64) -> (f64, &'static str) {
     }
 }
 
-/// Returns a tuple containing the value and the unit in bytes.  In units of 1000.
-/// This only supports up to a tera.  Note the "single" unit will have a space appended to match the others if
-/// `spacing` is true.
+/// Returns a tuple containing the value and the unit in bytes.  In units of
+/// 1000. This only supports up to a tera.  Note the "single" unit will have a
+/// space appended to match the others if `spacing` is true.
 #[inline]
 pub fn get_decimal_bytes(bytes: u64) -> (f64, &'static str) {
     match bytes {
@@ -67,8 +67,8 @@ pub fn get_decimal_bytes(bytes: u64) -> (f64, &'static str) {
 }
 
 /// Returns a tuple containing the value and the unit.  In units of 1024.
-/// This only supports up to a tebi.  Note the "single" unit will have a space appended to match the others if
-/// `spacing` is true.
+/// This only supports up to a tebi.  Note the "single" unit will have a space
+/// appended to match the others if `spacing` is true.
 #[inline]
 pub fn get_binary_prefix(quantity: u64, unit: &str) -> (f64, String) {
     match quantity {
@@ -81,8 +81,8 @@ pub fn get_binary_prefix(quantity: u64, unit: &str) -> (f64, String) {
 }
 
 /// Returns a tuple containing the value and the unit.  In units of 1000.
-/// This only supports up to a tera.  Note the "single" unit will have a space appended to match the others if
-/// `spacing` is true.
+/// This only supports up to a tera.  Note the "single" unit will have a space
+/// appended to match the others if `spacing` is true.
 #[inline]
 pub fn get_decimal_prefix(quantity: u64, unit: &str) -> (f64, String) {
     match quantity {
diff --git a/src/utils/general.rs b/src/utils/general.rs
index 789c73f5..7191f3cc 100644
--- a/src/utils/general.rs
+++ b/src/utils/general.rs
@@ -26,12 +26,12 @@ pub fn partial_ordering_desc<T: PartialOrd>(a: T, b: T) -> Ordering {
 
 /// A trait for additional clamping functions on numeric types.
 pub trait ClampExt {
-    /// Restrict a value by a lower bound. If the current value is _lower_ than `lower_bound`,
-    /// it will be set to `_lower_bound`.
+    /// Restrict a value by a lower bound. If the current value is _lower_ than
+    /// `lower_bound`, it will be set to `_lower_bound`.
     fn clamp_lower(&self, lower_bound: Self) -> Self;
 
-    /// Restrict a value by an upper bound. If the current value is _greater_ than `upper_bound`,
-    /// it will be set to `upper_bound`.
+    /// Restrict a value by an upper bound. If the current value is _greater_
+    /// than `upper_bound`, it will be set to `upper_bound`.
     fn clamp_upper(&self, upper_bound: Self) -> Self;
 }
 
diff --git a/src/utils/logging.rs b/src/utils/logging.rs
index 00e7e3db..27fa92fe 100644
--- a/src/utils/logging.rs
+++ b/src/utils/logging.rs
@@ -13,13 +13,16 @@ pub fn init_logger(
             let offset = OFFSET.get_or_init(|| {
                 use time::util::local_offset::Soundness;
 
-                // SAFETY: We only invoke this once, quickly, and it should be invoked in a single-thread context.
-                // We also should only ever hit this logging at all in a debug context which is generally fine,
+                // SAFETY: We only invoke this once, quickly, and it should be invoked in a
+                // single-thread context. We also should only ever hit this
+                // logging at all in a debug context which is generally fine,
                 // release builds should have this logging disabled entirely for now.
                 unsafe {
-                    // XXX: If we ever DO add general logging as a release feature, evaluate this again and whether this is
-                    // something we want enabled in release builds! What might be safe is falling back to the non-set-soundness
-                    // mode when specifically using certain feature flags (e.g. dev-logging feature enables this behaviour).
+                    // XXX: If we ever DO add general logging as a release feature, evaluate this
+                    // again and whether this is something we want enabled in
+                    // release builds! What might be safe is falling back to the non-set-soundness
+                    // mode when specifically using certain feature flags (e.g. dev-logging feature
+                    // enables this behaviour).
 
                     time::util::local_offset::set_soundness(Soundness::Unsound);
                     let res =
@@ -39,8 +42,8 @@ pub fn init_logger(
                 "{}[{}][{}] {}",
                 offset_time
                     .format(&time::macros::format_description!(
-                        // The weird "[[[" is because we need to escape a bracket ("[[") to show one "[".
-                        // See https://time-rs.github.io/book/api/format-description.html
+                        // The weird "[[[" is because we need to escape a bracket ("[[") to show
+                        // one "[". See https://time-rs.github.io/book/api/format-description.html
                         "[[[year]-[month]-[day]][[[hour]:[minute]:[second][subsecond digits:9]]"
                     ))
                     .unwrap(),
diff --git a/src/utils/strings.rs b/src/utils/strings.rs
index 1d970c9f..078f093b 100644
--- a/src/utils/strings.rs
+++ b/src/utils/strings.rs
@@ -9,11 +9,12 @@ pub fn truncate_to_text<'a, U: Into<usize>>(content: &str, width: U) -> Text<'a>
     Text::raw(truncate_str(content, width.into()))
 }
 
-/// Checks that the first string is equal to any of the other ones in a ASCII case-insensitive match.
+/// Checks that the first string is equal to any of the other ones in a ASCII
+/// case-insensitive match.
 ///
 /// The generated code is the same as writing:
-/// `to_ascii_lowercase(a) == to_ascii_lowercase(b) || to_ascii_lowercase(a) == to_ascii_lowercase(c)`,
-/// but without allocating and copying temporaries.
+/// `to_ascii_lowercase(a) == to_ascii_lowercase(b) || to_ascii_lowercase(a) ==
+/// to_ascii_lowercase(c)`, but without allocating and copying temporaries.
 ///
 /// # Examples
 ///
diff --git a/src/widgets.rs b/src/widgets.rs
index 9c0a69be..b4f82650 100644
--- a/src/widgets.rs
+++ b/src/widgets.rs
@@ -15,7 +15,8 @@ pub use process_table::*;
 pub use temperature_table::*;
 use tui::{layout::Rect, Frame};
 
-/// A [`Widget`] converts raw data into something that a user can see and interact with.
+/// A [`Widget`] converts raw data into something that a user can see and
+/// interact with.
 pub trait Widget<Data> {
     /// How to actually draw the widget to the terminal.
     fn draw(&self, f: &mut Frame<'_>, draw_location: Rect, widget_id: u64);
diff --git a/src/widgets/cpu_graph.rs b/src/widgets/cpu_graph.rs
index e90ce697..889cf57e 100644
--- a/src/widgets/cpu_graph.rs
+++ b/src/widgets/cpu_graph.rs
@@ -87,13 +87,13 @@ impl DataToCell<CpuWidgetColumn> for CpuWidgetTableData {
 
         let calculated_width = calculated_width.get();
 
-        // This is a bit of a hack, but apparently we can avoid having to do any fancy checks
-        // of showing the "All" on a specific column if the other is hidden by just always
-        // showing it on the CPU (first) column - if there isn't room for it, it will just collapse
-        // down.
+        // This is a bit of a hack, but apparently we can avoid having to do any fancy
+        // checks of showing the "All" on a specific column if the other is
+        // hidden by just always showing it on the CPU (first) column - if there
+        // isn't room for it, it will just collapse down.
         //
-        // This is the same for the use percentages - we just *always* show them, and *always* hide the CPU column if
-        // it is too small.
+        // This is the same for the use percentages - we just *always* show them, and
+        // *always* hide the CPU column if it is too small.
         match &self {
             CpuWidgetTableData::All => match column {
                 CpuWidgetColumn::CPU => Some("All".into()),
diff --git a/src/widgets/process_table.rs b/src/widgets/process_table.rs
index 0b081dec..873a8d8a 100644
--- a/src/widgets/process_table.rs
+++ b/src/widgets/process_table.rs
@@ -28,7 +28,8 @@ use crate::{
     data_collection::processes::{Pid, ProcessHarvest},
 };
 
-/// ProcessSearchState only deals with process' search's current settings and state.
+/// ProcessSearchState only deals with process' search's current settings and
+/// state.
 pub struct ProcessSearchState {
     pub search_state: AppSearchState,
     pub is_ignoring_case: bool,
@@ -172,7 +173,8 @@ pub struct ProcWidgetState {
     /// The state of the togglable table that controls sorting.
     pub sort_table: SortTable,
 
-    /// The internal column mapping as an [`IndexSet`], to allow us to do quick mappings of column type -> index.
+    /// The internal column mapping as an [`IndexSet`], to allow us to do quick
+    /// mappings of column type -> index.
     pub column_mapping: IndexSet<ProcWidgetColumn>,
 
     /// A name-to-pid mapping.
@@ -426,8 +428,9 @@ impl ProcWidgetState {
 
     /// Update the current table data.
     ///
-    /// This function *only* updates the displayed process data. If there is a need to update the actual *stored* data,
-    /// call it before this function.
+    /// This function *only* updates the displayed process data. If there is a
+    /// need to update the actual *stored* data, call it before this
+    /// function.
     pub fn set_table_data(&mut self, data_collection: &DataCollection) {
         let data = match &self.mode {
             ProcWidgetMode::Grouped | ProcWidgetMode::Normal => {
@@ -495,7 +498,8 @@ impl ProcWidgetState {
             }
         }
 
-        // A process is shown under the filtered tree if at least one of these conditions hold:
+        // A process is shown under the filtered tree if at least one of these
+        // conditions hold:
         // - The process itself matches.
         // - The process contains some descendant that matches.
         // - The process's parent (and only parent, not any ancestor) matches.
@@ -524,7 +528,8 @@ impl ProcWidgetState {
 
                         // Show the entry if it is:
                         // - Matches the filter.
-                        // - Has at least one child (doesn't have to be direct) that matches the filter.
+                        // - Has at least one child (doesn't have to be direct) that matches the
+                        //   filter.
                         // - Is the child of a shown process.
                         let is_shown = is_process_matching
                             || !shown_children.is_empty()
@@ -724,7 +729,8 @@ impl ProcWidgetState {
                 if let Some(grouped_process_harvest) = id_process_mapping.get_mut(id) {
                     grouped_process_harvest.add(process);
                 } else {
-                    // FIXME: [PERF] could maybe eliminate an allocation here in the grouped mode... or maybe just avoid the entire transformation step, making an alloc fine.
+                    // FIXME: [PERF] could maybe eliminate an allocation here in the grouped mode...
+                    // or maybe just avoid the entire transformation step, making an alloc fine.
                     id_process_mapping.insert(id, process.clone());
                 }
             }
@@ -813,8 +819,8 @@ impl ProcWidgetState {
         self.force_update_data = true;
     }
 
-    /// Marks the selected column as hidden, and automatically resets the selected column to the default
-    /// sort index and order.
+    /// Marks the selected column as hidden, and automatically resets the
+    /// selected column to the default sort index and order.
     fn hide_column(&mut self, column: ProcWidgetColumn) {
         if let Some(index) = self.column_mapping.get_index_of(&column) {
             if let Some(col) = self.table.columns.get_mut(index) {
@@ -837,7 +843,8 @@ impl ProcWidgetState {
         }
     }
 
-    /// Select a column. If the column is already selected, then just toggle the sort order.
+    /// Select a column. If the column is already selected, then just toggle the
+    /// sort order.
     pub fn select_column(&mut self, column: ProcWidgetColumn) {
         if let Some(index) = self.column_mapping.get_index_of(&column) {
             self.table.set_sort_index(index);
@@ -891,12 +898,15 @@ impl ProcWidgetState {
 
     /// Toggles the appropriate columns/settings when tab is pressed.
     ///
-    /// If count is enabled, we should set the mode to [`ProcWidgetMode::Grouped`], and switch off the User and State
-    /// columns. We should also move the user off of the columns if they were selected, as those columns are now hidden
-    /// (handled by internal method calls), and go back to the "defaults".
+    /// If count is enabled, we should set the mode to
+    /// [`ProcWidgetMode::Grouped`], and switch off the User and State
+    /// columns. We should also move the user off of the columns if they were
+    /// selected, as those columns are now hidden (handled by internal
+    /// method calls), and go back to the "defaults".
     ///
-    /// Otherwise, if count is disabled, then if the columns exist, the User and State columns should be re-enabled,
-    /// and the mode switched to [`ProcWidgetMode::Normal`].
+    /// Otherwise, if count is disabled, then if the columns exist, the User and
+    /// State columns should be re-enabled, and the mode switched to
+    /// [`ProcWidgetMode::Normal`].
     pub fn toggle_tab(&mut self) {
         if !matches!(self.mode, ProcWidgetMode::Tree { .. }) {
             if let Some(index) = self
@@ -1005,14 +1015,15 @@ impl ProcWidgetState {
         self.proc_search.search_state.walk_backward();
     }
 
-    /// Returns the number of columns *enabled*. Note this differs from *visible* - a column may be enabled but not
-    /// visible (e.g. off screen).
+    /// Returns the number of columns *enabled*. Note this differs from
+    /// *visible* - a column may be enabled but not visible (e.g. off
+    /// screen).
     pub fn num_enabled_columns(&self) -> usize {
         self.table.columns.iter().filter(|c| !c.is_hidden).count()
     }
 
-    /// Sets the [`ProcWidgetState`]'s current sort index to whatever was in the sort table if possible, then closes the
-    /// sort table.
+    /// Sets the [`ProcWidgetState`]'s current sort index to whatever was in the
+    /// sort table if possible, then closes the sort table.
     pub(crate) fn use_sort_table_value(&mut self) {
         self.table.set_sort_index(self.sort_table.current_index());
 
@@ -1425,8 +1436,9 @@ mod test {
 
     /// Tests toggling if both mem and mem% columns are configured.
     ///
-    /// Currently, this test doesn't really do much, since we treat these two columns as the same - this test is
-    /// intended for use later when we might allow both at the same time.
+    /// Currently, this test doesn't really do much, since we treat these two
+    /// columns as the same - this test is intended for use later when we
+    /// might allow both at the same time.
     #[test]
     fn double_memory_sim_toggle() {
         let init_columns = [
@@ -1461,8 +1473,9 @@ mod test {
 
     /// Tests toggling if both pid and count columns are configured.
     ///
-    /// Currently, this test doesn't really do much, since we treat these two columns as the same - this test is
-    /// intended for use later when we might allow both at the same time.
+    /// Currently, this test doesn't really do much, since we treat these two
+    /// columns as the same - this test is intended for use later when we
+    /// might allow both at the same time.
     #[test]
     fn pid_and_count_sim_toggle() {
         let init_columns = [
@@ -1498,8 +1511,9 @@ mod test {
 
     /// Tests toggling if both command and name columns are configured.
     ///
-    /// Currently, this test doesn't really do much, since we treat these two columns as the same - this test is
-    /// intended for use later when we might allow both at the same time.
+    /// Currently, this test doesn't really do much, since we treat these two
+    /// columns as the same - this test is intended for use later when we
+    /// might allow both at the same time.
     #[test]
     fn command_name_sim_toggle() {
         let init_columns = [
diff --git a/src/widgets/process_table/proc_widget_data.rs b/src/widgets/process_table/proc_widget_data.rs
index 034c36e5..d2eb6b97 100644
--- a/src/widgets/process_table/proc_widget_data.rs
+++ b/src/widgets/process_table/proc_widget_data.rs
@@ -41,9 +41,9 @@ impl From<&'static str> for Id {
 }
 
 impl Id {
-    /// Returns the ID as a lowercase [`String`], with no prefix. This is primarily useful for
-    /// cases like sorting where we treat everything as the same case (e.g. `Discord` comes before
-    /// `dkms`).
+    /// Returns the ID as a lowercase [`String`], with no prefix. This is
+    /// primarily useful for cases like sorting where we treat everything as
+    /// the same case (e.g. `Discord` comes before `dkms`).
     pub fn to_lowercase(&self) -> String {
         match &self.id_type {
             IdType::Name(name) => name.to_lowercase(),
@@ -306,7 +306,8 @@ impl DataToCell<ProcColumn> for ProcWidgetData {
         let calculated_width = calculated_width.get();
 
         // TODO: Optimize the string allocations here...
-        // TODO: Also maybe just pull in the to_string call but add a variable for the differences.
+        // TODO: Also maybe just pull in the to_string call but add a variable for the
+        // differences.
         Some(match column {
             ProcColumn::CpuPercent => format!("{:.1}%", self.cpu_usage_percent).into(),
             ProcColumn::MemoryVal | ProcColumn::MemoryPercent => self.mem_usage.to_string().into(),
diff --git a/tests/integration/arg_tests.rs b/tests/integration/arg_tests.rs
index f0f4ec3b..e8804421 100644
--- a/tests/integration/arg_tests.rs
+++ b/tests/integration/arg_tests.rs
@@ -1,4 +1,5 @@
-//! 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.
 
 use assert_cmd::prelude::*;
 use predicates::prelude::*;
diff --git a/tests/integration/util.rs b/tests/integration/util.rs
index ce77bed6..350b7da9 100644
--- a/tests/integration/util.rs
+++ b/tests/integration/util.rs
@@ -19,13 +19,14 @@ fn get_qemu_target(arch: &str) -> &str {
     }
 }
 
-/// This is required since running binary tests via cross can cause be tricky! We need to basically "magically" grab
-/// the correct runner in some cases, which can be done by inspecting env variables that should only show up while
+/// This is required since running binary tests via cross can cause be tricky!
+/// We need to basically "magically" grab the correct runner in some cases,
+/// which can be done by inspecting env variables that should only show up while
 /// using cross.
 ///
 /// Originally inspired by [ripgrep's test files](https://cs.github.com/BurntSushi/ripgrep/blob/9f0e88bcb14e02da1b88872435b17d74786640b5/tests/util.rs#L470),
-/// but adapted to work more generally with the architectures supported by bottom after looking through cross'
-/// [linux-runner](https://github.com/cross-rs/cross/blob/main/docker/linux-runner) file.
+/// but adapted to work more generally with the architectures supported by
+/// bottom after looking through cross' [linux-runner](https://github.com/cross-rs/cross/blob/main/docker/linux-runner) file.
 fn cross_runner() -> Option<String> {
     const TARGET_RUNNER: &str = "CARGO_TARGET_RUNNER";
     const CROSS_RUNNER: &str = "CROSS_RUNNER";