diff --git a/src/app/data_farmer.rs b/src/app/data_farmer.rs index d2edb53a..46eb32f2 100644 --- a/src/app/data_farmer.rs +++ b/src/app/data_farmer.rs @@ -21,7 +21,8 @@ use hashbrown::HashMap; use crate::data_harvester::batteries; use crate::{ data_harvester::{cpu, disks, memory, network, processes::ProcessHarvest, temperature, Data}, - utils::gen_util::{get_decimal_bytes, GIGA_LIMIT}, + utils::data_prefixes::*, + utils::gen_util::get_decimal_bytes, Pid, }; diff --git a/src/app/data_harvester/disks/unix/file_systems.rs b/src/app/data_harvester/disks/unix/file_systems.rs index ba6c5689..15bdc3ac 100644 --- a/src/app/data_harvester/disks/unix/file_systems.rs +++ b/src/app/data_harvester/disks/unix/file_systems.rs @@ -1,5 +1,7 @@ use std::str::FromStr; +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). /// @@ -124,9 +126,9 @@ impl FromStr for FileSystem { FileSystem::Ext3 } else if s.eq_ignore_ascii_case("ext4") { FileSystem::Ext4 - } else if s.eq_ignore_ascii_case("msdos") || s.eq_ignore_ascii_case("vfat") { + } else if multi_eq_ignore_ascii_case!(s, "msdos" | "vfat") { FileSystem::VFat - } else if s.eq_ignore_ascii_case("ntfs3") || s.eq_ignore_ascii_case("ntfs") { + } else if multi_eq_ignore_ascii_case!(s, "ntfs3" | "ntfs") { FileSystem::Ntfs } else if s.eq_ignore_ascii_case("zfs") { FileSystem::Zfs diff --git a/src/app/query.rs b/src/app/query.rs index 85858ddc..97809a0e 100644 --- a/src/app/query.rs +++ b/src/app/query.rs @@ -1,10 +1,13 @@ -use std::fmt::Debug; +use std::fmt::{Debug, Formatter}; use std::time::Duration; use std::{borrow::Cow, collections::VecDeque}; use humantime::parse_duration; +use regex::Regex; use super::data_harvester::processes::ProcessHarvest; +use crate::multi_eq_ignore_ascii_case; +use crate::utils::data_prefixes::*; use crate::utils::error::{ BottomError::{self, QueryError}, Result, @@ -54,8 +57,8 @@ pub fn parse_query( let mut rhs: Option> = None; while let Some(queue_top) = query.front() { - // debug!("OR QT: {queue_top:?}"); - if OR_LIST.contains(&queue_top.to_lowercase().as_str()) { + let current_lowercase = queue_top.to_lowercase(); + if OR_LIST.contains(¤t_lowercase.as_str()) { query.pop_front(); rhs = Some(Box::new(process_and(query)?)); @@ -75,7 +78,7 @@ pub fn parse_query( } else { break; } - } else if COMPARISON_LIST.contains(&queue_top.to_lowercase().as_str()) { + } else if COMPARISON_LIST.contains(¤t_lowercase.as_str()) { return Err(QueryError(Cow::Borrowed("Comparison not valid here"))); } else { break; @@ -90,8 +93,8 @@ pub fn parse_query( let mut rhs: Option> = None; while let Some(queue_top) = query.front() { - // debug!("AND QT: {queue_top:?}"); - if AND_LIST.contains(&queue_top.to_lowercase().as_str()) { + let current_lowercase = queue_top.to_lowercase(); + if AND_LIST.contains(¤t_lowercase.as_str()) { query.pop_front(); rhs = Some(Box::new(process_prefix(query, false)?)); @@ -114,7 +117,7 @@ pub fn parse_query( } else { break; } - } else if COMPARISON_LIST.contains(&queue_top.to_lowercase().as_str()) { + } else if COMPARISON_LIST.contains(¤t_lowercase.as_str()) { return Err(QueryError(Cow::Borrowed("Comparison not valid here"))); } else { break; @@ -387,49 +390,44 @@ pub fn parse_query( | PrefixType::TRead | PrefixType::TWrite => { // If no unit, assume base. + // // Furthermore, base must be PEEKED at initially, and will // require (likely) prefix_type specific checks // Lastly, if it *is* a unit, remember to POP! - if let Some(potential_unit) = query.front() { - match potential_unit.to_lowercase().as_str() { - "tb" => { - value *= 1_000_000_000_000.0; - query.pop_front(); - } - "tib" => { - value *= 1_099_511_627_776.0; - query.pop_front(); - } - "gb" => { - value *= 1_000_000_000.0; - query.pop_front(); - } - "gib" => { - value *= 1_073_741_824.0; - query.pop_front(); - } - "mb" => { - value *= 1_000_000.0; - query.pop_front(); - } - "mib" => { - value *= 1_048_576.0; - query.pop_front(); - } - "kb" => { - value *= 1000.0; - query.pop_front(); - } - "kib" => { - value *= 1024.0; - query.pop_front(); - } - "b" => { - // Just gotta pop. - query.pop_front(); - } - _ => {} + if potential_unit.eq_ignore_ascii_case("tb") { + value *= TERA_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("tib") + { + value *= TEBI_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("gb") + { + value *= GIGA_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("gib") + { + value *= GIBI_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("mb") + { + value *= MEGA_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("mib") + { + value *= MEBI_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("kb") + { + value *= KILO_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("kib") + { + value *= KIBI_LIMIT_F64; + query.pop_front(); + } else if potential_unit.eq_ignore_ascii_case("b") { + query.pop_front(); } } } @@ -466,7 +464,7 @@ pub fn parse_query( let mut split_query = VecDeque::new(); search_query.split_whitespace().for_each(|s| { - // From https://stackoverflow.com/a/56923739 in order to get a split but include the parentheses + // From https://stackoverflow.com/a/56923739 in order to get a split, but include the parentheses let mut last = 0; for (index, matched) in s.match_indices(|x| DELIMITER_LIST.contains(&x)) { if last != index { @@ -519,7 +517,7 @@ impl Query { } impl Debug for Query { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!("{:?}", self.query)) } } @@ -561,7 +559,7 @@ impl Or { } impl Debug for Or { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match &self.rhs { Some(rhs) => f.write_fmt(format_args!("({:?} OR {:?})", self.lhs, rhs)), None => f.write_fmt(format_args!("{:?}", self.lhs)), @@ -606,7 +604,7 @@ impl And { } impl Debug for And { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match &self.rhs { Some(rhs) => f.write_fmt(format_args!("({:?} AND {:?})", self.lhs, rhs)), None => f.write_fmt(format_args!("{:?}", self.lhs)), @@ -637,23 +635,35 @@ impl std::str::FromStr for PrefixType { fn from_str(s: &str) -> Result { use PrefixType::*; - let lower_case = s.to_lowercase(); - // Didn't add mem_bytes, total_read, and total_write + // TODO: Didn't add mem_bytes, total_read, and total_write // for now as it causes help to be clogged. - match lower_case.as_str() { - "cpu" | "cpu%" => Ok(PCpu), - "mem" | "mem%" => Ok(PMem), - "memb" => Ok(MemBytes), - "read" | "r/s" | "rps" => Ok(Rps), - "write" | "w/s" | "wps" => Ok(Wps), - "tread" | "t.read" => Ok(TRead), - "twrite" | "t.write" => Ok(TWrite), - "pid" => Ok(Pid), - "state" => Ok(State), - "user" => Ok(User), - "time" => Ok(Time), - _ => Ok(Name), - } + let result = if multi_eq_ignore_ascii_case!(s, "cpu" | "cpu%") { + PCpu + } else if multi_eq_ignore_ascii_case!(s, "mem" | "mem%") { + PMem + } else if multi_eq_ignore_ascii_case!(s, "memb") { + MemBytes + } else if multi_eq_ignore_ascii_case!(s, "read" | "r/s" | "rps") { + Rps + } else if multi_eq_ignore_ascii_case!(s, "write" | "w/s" | "wps") { + Wps + } else if multi_eq_ignore_ascii_case!(s, "tread" | "t.read") { + TRead + } else if multi_eq_ignore_ascii_case!(s, "twrite" | "t.write") { + TWrite + } else if multi_eq_ignore_ascii_case!(s, "pid") { + Pid + } else if multi_eq_ignore_ascii_case!(s, "state") { + State + } else if multi_eq_ignore_ascii_case!(s, "user") { + User + } else if multi_eq_ignore_ascii_case!(s, "time") { + Time + } else { + Name + }; + + Ok(result) } } @@ -698,7 +708,7 @@ impl Prefix { if let Some((taken_pt, _)) = taken_pwc { self.regex_prefix = Some(( taken_pt, - StringQuery::Regex(regex::Regex::new(final_regex_string)?), + StringQuery::Regex(Regex::new(final_regex_string)?), )); } } @@ -808,7 +818,7 @@ impl Prefix { } impl Debug for Prefix { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Some(or) = &self.or { f.write_fmt(format_args!("{or:?}")) } else if let Some(regex_prefix) = &self.regex_prefix { @@ -816,7 +826,7 @@ impl Debug for Prefix { } else if let Some(compare_prefix) = &self.compare_prefix { f.write_fmt(format_args!("{compare_prefix:?}")) } else { - f.write_fmt(format_args!("")) + f.write_str("") } } } @@ -833,7 +843,7 @@ pub enum QueryComparison { #[derive(Debug)] pub enum StringQuery { Value(String), - Regex(regex::Regex), + Regex(Regex), } #[derive(Debug)] diff --git a/src/canvas/widgets/network_graph.rs b/src/canvas/widgets/network_graph.rs index 5073fda6..d21a4698 100644 --- a/src/canvas/widgets/network_graph.rs +++ b/src/canvas/widgets/network_graph.rs @@ -14,7 +14,7 @@ use crate::{ time_graph::{GraphData, TimeGraph}, tui_widget::time_chart::Point, }, - utils::{data_units::DataUnit, gen_util::*}, + utils::{data_prefixes::*, data_units::DataUnit, gen_util::partial_ordering}, }; impl Painter { diff --git a/src/data_conversion.rs b/src/data_conversion.rs index 782f6c20..645e70f3 100644 --- a/src/data_conversion.rs +++ b/src/data_conversion.rs @@ -10,6 +10,7 @@ use crate::app::{ AxisScaling, }; use crate::components::tui_widget::time_chart::Point; +use crate::utils::data_prefixes::*; use crate::utils::data_units::DataUnit; use crate::utils::gen_util::*; use crate::widgets::{DiskWidgetData, TempWidgetData}; diff --git a/src/lib.rs b/src/lib.rs index 1c804181..b0944b7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,6 +47,7 @@ use utils::error; pub mod app; pub mod utils { + pub mod data_prefixes; pub mod data_units; pub mod error; pub mod gen_util; diff --git a/src/utils/data_prefixes.rs b/src/utils/data_prefixes.rs new file mode 100644 index 00000000..62a3edd6 --- /dev/null +++ b/src/utils/data_prefixes.rs @@ -0,0 +1,39 @@ +pub const KILO_LIMIT: u64 = 1000; +pub const MEGA_LIMIT: u64 = 1_000_000; +pub const GIGA_LIMIT: u64 = 1_000_000_000; +pub const TERA_LIMIT: u64 = 1_000_000_000_000; +pub const KIBI_LIMIT: u64 = 1024; +pub const MEBI_LIMIT: u64 = 1024 * 1024; +pub const GIBI_LIMIT: u64 = 1024 * 1024 * 1024; +pub const TEBI_LIMIT: u64 = 1024 * 1024 * 1024 * 1024; + +pub const KILO_LIMIT_F64: f64 = 1000.0; +pub const MEGA_LIMIT_F64: f64 = 1_000_000.0; +pub const GIGA_LIMIT_F64: f64 = 1_000_000_000.0; +pub const TERA_LIMIT_F64: f64 = 1_000_000_000_000.0; +pub const KIBI_LIMIT_F64: f64 = 1024.0; +pub const MEBI_LIMIT_F64: f64 = 1024.0 * 1024.0; +pub const GIBI_LIMIT_F64: f64 = 1024.0 * 1024.0 * 1024.0; +pub const TEBI_LIMIT_F64: f64 = 1024.0 * 1024.0 * 1024.0 * 1024.0; + +pub const LOG_KILO_LIMIT: f64 = 3.0; +pub const LOG_MEGA_LIMIT: f64 = 6.0; +pub const LOG_GIGA_LIMIT: f64 = 9.0; +pub const LOG_TERA_LIMIT: f64 = 12.0; +pub const LOG_PETA_LIMIT: f64 = 15.0; + +pub const LOG_KIBI_LIMIT: f64 = 10.0; +pub const LOG_MEBI_LIMIT: f64 = 20.0; +pub const LOG_GIBI_LIMIT: f64 = 30.0; +pub const LOG_TEBI_LIMIT: f64 = 40.0; +pub const LOG_PEBI_LIMIT: f64 = 50.0; + +pub const LOG_KILO_LIMIT_U32: u32 = 3; +pub const LOG_MEGA_LIMIT_U32: u32 = 6; +pub const LOG_GIGA_LIMIT_U32: u32 = 9; +pub const LOG_TERA_LIMIT_U32: u32 = 12; + +pub const LOG_KIBI_LIMIT_U32: u32 = 10; +pub const LOG_MEBI_LIMIT_U32: u32 = 20; +pub const LOG_GIBI_LIMIT_U32: u32 = 30; +pub const LOG_TEBI_LIMIT_U32: u32 = 40; diff --git a/src/utils/gen_util.rs b/src/utils/gen_util.rs index 3a729f2e..ae35da8d 100644 --- a/src/utils/gen_util.rs +++ b/src/utils/gen_util.rs @@ -4,45 +4,7 @@ use tui::text::{Line, Span, Text}; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; -pub const KILO_LIMIT: u64 = 1000; -pub const MEGA_LIMIT: u64 = 1_000_000; -pub const GIGA_LIMIT: u64 = 1_000_000_000; -pub const TERA_LIMIT: u64 = 1_000_000_000_000; -pub const KIBI_LIMIT: u64 = 1024; -pub const MEBI_LIMIT: u64 = 1024 * 1024; -pub const GIBI_LIMIT: u64 = 1024 * 1024 * 1024; -pub const TEBI_LIMIT: u64 = 1024 * 1024 * 1024 * 1024; - -pub const KILO_LIMIT_F64: f64 = 1000.0; -pub const MEGA_LIMIT_F64: f64 = 1_000_000.0; -pub const GIGA_LIMIT_F64: f64 = 1_000_000_000.0; -pub const TERA_LIMIT_F64: f64 = 1_000_000_000_000.0; -pub const KIBI_LIMIT_F64: f64 = 1024.0; -pub const MEBI_LIMIT_F64: f64 = 1024.0 * 1024.0; -pub const GIBI_LIMIT_F64: f64 = 1024.0 * 1024.0 * 1024.0; -pub const TEBI_LIMIT_F64: f64 = 1024.0 * 1024.0 * 1024.0 * 1024.0; - -pub const LOG_KILO_LIMIT: f64 = 3.0; -pub const LOG_MEGA_LIMIT: f64 = 6.0; -pub const LOG_GIGA_LIMIT: f64 = 9.0; -pub const LOG_TERA_LIMIT: f64 = 12.0; -pub const LOG_PETA_LIMIT: f64 = 15.0; - -pub const LOG_KIBI_LIMIT: f64 = 10.0; -pub const LOG_MEBI_LIMIT: f64 = 20.0; -pub const LOG_GIBI_LIMIT: f64 = 30.0; -pub const LOG_TEBI_LIMIT: f64 = 40.0; -pub const LOG_PEBI_LIMIT: f64 = 50.0; - -pub const LOG_KILO_LIMIT_U32: u32 = 3; -pub const LOG_MEGA_LIMIT_U32: u32 = 6; -pub const LOG_GIGA_LIMIT_U32: u32 = 9; -pub const LOG_TERA_LIMIT_U32: u32 = 12; - -pub const LOG_KIBI_LIMIT_U32: u32 = 10; -pub const LOG_MEBI_LIMIT_U32: u32 = 20; -pub const LOG_GIBI_LIMIT_U32: u32 = 30; -pub const LOG_TEBI_LIMIT_U32: u32 = 40; +use super::data_prefixes::*; /// 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 @@ -201,6 +163,29 @@ pub fn partial_ordering_desc(a: T, b: T) -> Ordering { partial_ordering(a, b).reverse() } +/// 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. +/// +/// # Examples +/// +/// ```ignore +/// assert!(multi_eq_ignore_ascii_case!("test", "test")); +/// assert!(multi_eq_ignore_ascii_case!("test", "a" | "b" | "test")); +/// assert!(!multi_eq_ignore_ascii_case!("test", "a" | "b" | "c")); +/// ``` +#[macro_export] +macro_rules! multi_eq_ignore_ascii_case { + ( $lhs:expr, $last:literal ) => { + $lhs.eq_ignore_ascii_case($last) + }; + ( $lhs:expr, $head:literal | $($tail:tt)* ) => { + $lhs.eq_ignore_ascii_case($head) || multi_eq_ignore_ascii_case!($lhs, $($tail)*) + }; +} + #[cfg(test)] mod test { use super::*; @@ -382,4 +367,33 @@ mod test { assert_eq!(truncate_str(scientist, 1_usize), "…"); assert_eq!(truncate_str(scientist, 0_usize), ""); } + + #[test] + fn test_multi_eq_ignore_ascii_case() { + assert!( + multi_eq_ignore_ascii_case!("test", "test"), + "single comparison should succeed" + ); + assert!( + multi_eq_ignore_ascii_case!("test", "a" | "test"), + "double comparison should succeed" + ); + assert!( + multi_eq_ignore_ascii_case!("test", "a" | "b" | "test"), + "multi comparison should succeed" + ); + + assert!( + !multi_eq_ignore_ascii_case!("test", "a"), + "single non-matching should fail" + ); + assert!( + !multi_eq_ignore_ascii_case!("test", "a" | "b"), + "double non-matching should fail" + ); + assert!( + !multi_eq_ignore_ascii_case!("test", "a" | "b" | "c"), + "multi non-matching should fail" + ); + } }