mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-25 14:44:39 +02:00
refactor: error refactoring, part 1 (#1492)
* refactor: use anyhow for logging * refactor: clean up query errors * also remove tryparseint
This commit is contained in:
parent
df569b2319
commit
571a801bf8
167
src/app/query.rs
167
src/app/query.rs
@ -1,7 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
collections::VecDeque,
|
collections::VecDeque,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -9,17 +9,44 @@ use humantime::parse_duration;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
data_collection::processes::ProcessHarvest,
|
data_collection::processes::ProcessHarvest, multi_eq_ignore_ascii_case, utils::data_prefixes::*,
|
||||||
multi_eq_ignore_ascii_case,
|
|
||||||
utils::{
|
|
||||||
data_prefixes::*,
|
|
||||||
error::{
|
|
||||||
BottomError::{self, QueryError},
|
|
||||||
Result,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct QueryError {
|
||||||
|
reason: Cow<'static, str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QueryError {
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new<I: Into<Cow<'static, str>>>(reason: I) -> Self {
|
||||||
|
Self {
|
||||||
|
reason: reason.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn missing_value() -> Self {
|
||||||
|
Self {
|
||||||
|
reason: "Missing value".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for QueryError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.reason)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<regex::Error> for QueryError {
|
||||||
|
fn from(err: regex::Error) -> Self {
|
||||||
|
Self::new(err.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type QueryResult<T> = Result<T, QueryError>;
|
||||||
|
|
||||||
const DELIMITER_LIST: [char; 6] = ['=', '>', '<', '(', ')', '\"'];
|
const DELIMITER_LIST: [char; 6] = ['=', '>', '<', '(', ')', '\"'];
|
||||||
const COMPARISON_LIST: [&str; 3] = [">", "=", "<"];
|
const COMPARISON_LIST: [&str; 3] = [">", "=", "<"];
|
||||||
const OR_LIST: [&str; 2] = ["or", "||"];
|
const OR_LIST: [&str; 2] = ["or", "||"];
|
||||||
@ -47,11 +74,11 @@ const AND_LIST: [&str; 2] = ["and", "&&"];
|
|||||||
/// adjacent non-prefixed or quoted elements after splitting to treat as process
|
/// 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
|
/// names. Furthermore, we want to support boolean joiners like AND and OR, and
|
||||||
/// brackets.
|
/// brackets.
|
||||||
pub fn parse_query(
|
pub(crate) fn parse_query(
|
||||||
search_query: &str, is_searching_whole_word: bool, is_ignoring_case: bool,
|
search_query: &str, is_searching_whole_word: bool, is_ignoring_case: bool,
|
||||||
is_searching_with_regex: bool,
|
is_searching_with_regex: bool,
|
||||||
) -> Result<Query> {
|
) -> QueryResult<Query> {
|
||||||
fn process_string_to_filter(query: &mut VecDeque<String>) -> Result<Query> {
|
fn process_string_to_filter(query: &mut VecDeque<String>) -> QueryResult<Query> {
|
||||||
let lhs = process_or(query)?;
|
let lhs = process_or(query)?;
|
||||||
let mut list_of_ors = vec![lhs];
|
let mut list_of_ors = vec![lhs];
|
||||||
|
|
||||||
@ -62,7 +89,7 @@ pub fn parse_query(
|
|||||||
Ok(Query { query: list_of_ors })
|
Ok(Query { query: list_of_ors })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_or(query: &mut VecDeque<String>) -> Result<Or> {
|
fn process_or(query: &mut VecDeque<String>) -> QueryResult<Or> {
|
||||||
let mut lhs = process_and(query)?;
|
let mut lhs = process_and(query)?;
|
||||||
let mut rhs: Option<Box<And>> = None;
|
let mut rhs: Option<Box<And>> = None;
|
||||||
|
|
||||||
@ -89,7 +116,7 @@ pub fn parse_query(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if COMPARISON_LIST.contains(¤t_lowercase.as_str()) {
|
} else if COMPARISON_LIST.contains(¤t_lowercase.as_str()) {
|
||||||
return Err(QueryError(Cow::Borrowed("Comparison not valid here")));
|
return Err(QueryError::new("Comparison not valid here"));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -98,7 +125,7 @@ pub fn parse_query(
|
|||||||
Ok(Or { lhs, rhs })
|
Ok(Or { lhs, rhs })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_and(query: &mut VecDeque<String>) -> Result<And> {
|
fn process_and(query: &mut VecDeque<String>) -> QueryResult<And> {
|
||||||
let mut lhs = process_prefix(query, false)?;
|
let mut lhs = process_prefix(query, false)?;
|
||||||
let mut rhs: Option<Box<Prefix>> = None;
|
let mut rhs: Option<Box<Prefix>> = None;
|
||||||
|
|
||||||
@ -128,7 +155,7 @@ pub fn parse_query(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if COMPARISON_LIST.contains(¤t_lowercase.as_str()) {
|
} else if COMPARISON_LIST.contains(¤t_lowercase.as_str()) {
|
||||||
return Err(QueryError(Cow::Borrowed("Comparison not valid here")));
|
return Err(QueryError::new("Comparison not valid here"));
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -175,7 +202,7 @@ pub fn parse_query(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_prefix(query: &mut VecDeque<String>, inside_quotation: bool) -> Result<Prefix> {
|
fn process_prefix(query: &mut VecDeque<String>, inside_quotation: bool) -> QueryResult<Prefix> {
|
||||||
if let Some(queue_top) = query.pop_front() {
|
if let Some(queue_top) = query.pop_front() {
|
||||||
if inside_quotation {
|
if inside_quotation {
|
||||||
if queue_top == "\"" {
|
if queue_top == "\"" {
|
||||||
@ -210,7 +237,7 @@ pub fn parse_query(
|
|||||||
}
|
}
|
||||||
} else if queue_top == "(" {
|
} else if queue_top == "(" {
|
||||||
if query.is_empty() {
|
if query.is_empty() {
|
||||||
return Err(QueryError(Cow::Borrowed("Missing closing parentheses")));
|
return Err(QueryError::new("Missing closing parentheses"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut list_of_ors = VecDeque::new();
|
let mut list_of_ors = VecDeque::new();
|
||||||
@ -225,7 +252,7 @@ pub fn parse_query(
|
|||||||
|
|
||||||
// Ensure not empty
|
// Ensure not empty
|
||||||
if list_of_ors.is_empty() {
|
if list_of_ors.is_empty() {
|
||||||
return Err(QueryError("No values within parentheses group".into()));
|
return Err(QueryError::new("No values within parentheses group"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now convert this back to a OR...
|
// Now convert this back to a OR...
|
||||||
@ -264,13 +291,13 @@ pub fn parse_query(
|
|||||||
compare_prefix: None,
|
compare_prefix: None,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing closing parentheses".into()));
|
return Err(QueryError::new("Missing closing parentheses"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing closing parentheses".into()));
|
return Err(QueryError::new("Missing closing parentheses"));
|
||||||
}
|
}
|
||||||
} else if queue_top == ")" {
|
} else if queue_top == ")" {
|
||||||
return Err(QueryError("Missing opening parentheses".into()));
|
return Err(QueryError::new("Missing opening parentheses"));
|
||||||
} else if queue_top == "\"" {
|
} else if queue_top == "\"" {
|
||||||
// Similar to parentheses, trap and check for missing closing quotes. Note,
|
// Similar to parentheses, trap and check for missing closing quotes. Note,
|
||||||
// however, that we will DIRECTLY call another process_prefix
|
// however, that we will DIRECTLY call another process_prefix
|
||||||
@ -281,13 +308,13 @@ pub fn parse_query(
|
|||||||
if close_paren == "\"" {
|
if close_paren == "\"" {
|
||||||
return Ok(prefix);
|
return Ok(prefix);
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing closing quotation".into()));
|
return Err(QueryError::new("Missing closing quotation"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing closing quotation".into()));
|
return Err(QueryError::new("Missing closing quotation"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Get prefix type...
|
// Get prefix type.
|
||||||
let prefix_type = queue_top.parse::<PrefixType>()?;
|
let prefix_type = queue_top.parse::<PrefixType>()?;
|
||||||
let content = if let PrefixType::Name = prefix_type {
|
let content = if let PrefixType::Name = prefix_type {
|
||||||
Some(queue_top)
|
Some(queue_top)
|
||||||
@ -362,15 +389,15 @@ pub fn parse_query(
|
|||||||
duration_string = Some(queue_next);
|
duration_string = Some(queue_next);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing value".into()));
|
return Err(QueryError::missing_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(condition) = condition {
|
if let Some(condition) = condition {
|
||||||
let duration = parse_duration(
|
let duration = parse_duration(
|
||||||
&duration_string.ok_or(QueryError("Missing value".into()))?,
|
&duration_string.ok_or(QueryError::missing_value())?,
|
||||||
)
|
)
|
||||||
.map_err(|err| QueryError(err.to_string().into()))?;
|
.map_err(|err| QueryError::new(err.to_string()))?;
|
||||||
|
|
||||||
return Ok(Prefix {
|
return Ok(Prefix {
|
||||||
or: None,
|
or: None,
|
||||||
@ -399,7 +426,7 @@ pub fn parse_query(
|
|||||||
if let Some(queue_next) = query.pop_front() {
|
if let Some(queue_next) = query.pop_front() {
|
||||||
value = queue_next.parse::<f64>().ok();
|
value = queue_next.parse::<f64>().ok();
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing value".into()));
|
return Err(QueryError::missing_value());
|
||||||
}
|
}
|
||||||
} else if content == ">" || content == "<" {
|
} else if content == ">" || content == "<" {
|
||||||
// We also have to check if the next string is an "="...
|
// We also have to check if the next string is an "="...
|
||||||
@ -413,7 +440,7 @@ pub fn parse_query(
|
|||||||
if let Some(queue_next_next) = query.pop_front() {
|
if let Some(queue_next_next) = query.pop_front() {
|
||||||
value = queue_next_next.parse::<f64>().ok();
|
value = queue_next_next.parse::<f64>().ok();
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing value".into()));
|
return Err(QueryError::missing_value());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
condition = Some(if content == ">" {
|
condition = Some(if content == ">" {
|
||||||
@ -424,7 +451,7 @@ pub fn parse_query(
|
|||||||
value = queue_next.parse::<f64>().ok();
|
value = queue_next.parse::<f64>().ok();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing value".into()));
|
return Err(QueryError::missing_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -467,15 +494,15 @@ pub fn parse_query(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(QueryError("Missing argument for search prefix".into()));
|
return Err(QueryError::new("Missing argument for search prefix"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if inside_quotation {
|
} else if inside_quotation {
|
||||||
// Uh oh, it's empty with quotes!
|
// Uh oh, it's empty with quotes!
|
||||||
return Err(QueryError("Missing closing quotation".into()));
|
return Err(QueryError::new("Missing closing quotation"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(QueryError("Invalid query".into()))
|
Err(QueryError::new("Invalid query"))
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut split_query = VecDeque::new();
|
let mut split_query = VecDeque::new();
|
||||||
@ -507,14 +534,14 @@ pub fn parse_query(
|
|||||||
|
|
||||||
pub struct Query {
|
pub struct Query {
|
||||||
/// Remember, AND > OR, but AND must come after OR when we parse.
|
/// Remember, AND > OR, but AND must come after OR when we parse.
|
||||||
pub query: Vec<Or>,
|
query: Vec<Or>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Query {
|
impl Query {
|
||||||
pub fn process_regexes(
|
fn process_regexes(
|
||||||
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
||||||
is_searching_with_regex: bool,
|
is_searching_with_regex: bool,
|
||||||
) -> Result<()> {
|
) -> QueryResult<()> {
|
||||||
for or in &mut self.query {
|
for or in &mut self.query {
|
||||||
or.process_regexes(
|
or.process_regexes(
|
||||||
is_searching_whole_word,
|
is_searching_whole_word,
|
||||||
@ -526,7 +553,7 @@ impl Query {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
pub(crate) fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
||||||
self.query
|
self.query
|
||||||
.iter()
|
.iter()
|
||||||
.all(|ok| ok.check(process, is_using_command))
|
.all(|ok| ok.check(process, is_using_command))
|
||||||
@ -540,16 +567,16 @@ impl Debug for Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Or {
|
struct Or {
|
||||||
pub lhs: And,
|
lhs: And,
|
||||||
pub rhs: Option<Box<And>>,
|
rhs: Option<Box<And>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Or {
|
impl Or {
|
||||||
pub fn process_regexes(
|
fn process_regexes(
|
||||||
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
||||||
is_searching_with_regex: bool,
|
is_searching_with_regex: bool,
|
||||||
) -> Result<()> {
|
) -> QueryResult<()> {
|
||||||
self.lhs.process_regexes(
|
self.lhs.process_regexes(
|
||||||
is_searching_whole_word,
|
is_searching_whole_word,
|
||||||
is_ignoring_case,
|
is_ignoring_case,
|
||||||
@ -566,7 +593,7 @@ impl Or {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
||||||
if let Some(rhs) = &self.rhs {
|
if let Some(rhs) = &self.rhs {
|
||||||
self.lhs.check(process, is_using_command) || rhs.check(process, is_using_command)
|
self.lhs.check(process, is_using_command) || rhs.check(process, is_using_command)
|
||||||
} else {
|
} else {
|
||||||
@ -585,16 +612,16 @@ impl Debug for Or {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct And {
|
struct And {
|
||||||
pub lhs: Prefix,
|
lhs: Prefix,
|
||||||
pub rhs: Option<Box<Prefix>>,
|
rhs: Option<Box<Prefix>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl And {
|
impl And {
|
||||||
pub fn process_regexes(
|
fn process_regexes(
|
||||||
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
||||||
is_searching_with_regex: bool,
|
is_searching_with_regex: bool,
|
||||||
) -> Result<()> {
|
) -> QueryResult<()> {
|
||||||
self.lhs.process_regexes(
|
self.lhs.process_regexes(
|
||||||
is_searching_whole_word,
|
is_searching_whole_word,
|
||||||
is_ignoring_case,
|
is_ignoring_case,
|
||||||
@ -611,7 +638,7 @@ impl And {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
||||||
if let Some(rhs) = &self.rhs {
|
if let Some(rhs) = &self.rhs {
|
||||||
self.lhs.check(process, is_using_command) && rhs.check(process, is_using_command)
|
self.lhs.check(process, is_using_command) && rhs.check(process, is_using_command)
|
||||||
} else {
|
} else {
|
||||||
@ -630,7 +657,7 @@ impl Debug for And {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum PrefixType {
|
enum PrefixType {
|
||||||
Pid,
|
Pid,
|
||||||
PCpu,
|
PCpu,
|
||||||
MemBytes,
|
MemBytes,
|
||||||
@ -653,9 +680,9 @@ pub enum PrefixType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl std::str::FromStr for PrefixType {
|
impl std::str::FromStr for PrefixType {
|
||||||
type Err = BottomError;
|
type Err = QueryError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self> {
|
fn from_str(s: &str) -> QueryResult<Self> {
|
||||||
use PrefixType::*;
|
use PrefixType::*;
|
||||||
|
|
||||||
// TODO: Didn't add mem_bytes, total_read, and total_write
|
// TODO: Didn't add mem_bytes, total_read, and total_write
|
||||||
@ -702,17 +729,17 @@ impl std::str::FromStr for PrefixType {
|
|||||||
// TODO: This is also jank and could be better represented. Add tests, then
|
// TODO: This is also jank and could be better represented. Add tests, then
|
||||||
// clean up!
|
// clean up!
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Prefix {
|
struct Prefix {
|
||||||
pub or: Option<Box<Or>>,
|
or: Option<Box<Or>>,
|
||||||
pub regex_prefix: Option<(PrefixType, StringQuery)>,
|
regex_prefix: Option<(PrefixType, StringQuery)>,
|
||||||
pub compare_prefix: Option<(PrefixType, ComparableQuery)>,
|
compare_prefix: Option<(PrefixType, ComparableQuery)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Prefix {
|
impl Prefix {
|
||||||
pub fn process_regexes(
|
fn process_regexes(
|
||||||
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
&mut self, is_searching_whole_word: bool, is_ignoring_case: bool,
|
||||||
is_searching_with_regex: bool,
|
is_searching_with_regex: bool,
|
||||||
) -> Result<()> {
|
) -> QueryResult<()> {
|
||||||
if let Some(or) = &mut self.or {
|
if let Some(or) = &mut self.or {
|
||||||
return or.process_regexes(
|
return or.process_regexes(
|
||||||
is_searching_whole_word,
|
is_searching_whole_word,
|
||||||
@ -752,7 +779,7 @@ impl Prefix {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
fn check(&self, process: &ProcessHarvest, is_using_command: bool) -> bool {
|
||||||
fn matches_condition<I: Into<f64>, J: Into<f64>>(
|
fn matches_condition<I: Into<f64>, J: Into<f64>>(
|
||||||
condition: &QueryComparison, lhs: I, rhs: J,
|
condition: &QueryComparison, lhs: I, rhs: J,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
@ -883,7 +910,7 @@ impl Debug for Prefix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum QueryComparison {
|
enum QueryComparison {
|
||||||
Equal,
|
Equal,
|
||||||
Less,
|
Less,
|
||||||
Greater,
|
Greater,
|
||||||
@ -892,25 +919,25 @@ pub enum QueryComparison {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum StringQuery {
|
enum StringQuery {
|
||||||
Value(String),
|
Value(String),
|
||||||
Regex(Regex),
|
Regex(Regex),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ComparableQuery {
|
enum ComparableQuery {
|
||||||
Numerical(NumericalQuery),
|
Numerical(NumericalQuery),
|
||||||
Time(TimeQuery),
|
Time(TimeQuery),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NumericalQuery {
|
struct NumericalQuery {
|
||||||
pub condition: QueryComparison,
|
condition: QueryComparison,
|
||||||
pub value: f64,
|
value: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TimeQuery {
|
struct TimeQuery {
|
||||||
pub condition: QueryComparison,
|
condition: QueryComparison,
|
||||||
pub duration: Duration,
|
duration: Duration,
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,9 @@ impl UserTable {
|
|||||||
let passwd = unsafe { libc::getpwuid(uid) };
|
let passwd = unsafe { libc::getpwuid(uid) };
|
||||||
|
|
||||||
if passwd.is_null() {
|
if passwd.is_null() {
|
||||||
Err(error::BottomError::QueryError("Missing passwd".into()))
|
Err(error::BottomError::GenericError(
|
||||||
|
"passwd is inaccessible".into(),
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
// SAFETY: We return early if passwd is null.
|
// SAFETY: We return early if passwd is null.
|
||||||
let username = unsafe { std::ffi::CStr::from_ptr((*passwd).pw_name) }
|
let username = unsafe { std::ffi::CStr::from_ptr((*passwd).pw_name) }
|
||||||
|
@ -566,7 +566,10 @@ fn get_show_average_cpu(args: &BottomArgs, config: &ConfigV1) -> bool {
|
|||||||
|
|
||||||
fn try_parse_ms(s: &str) -> error::Result<u64> {
|
fn try_parse_ms(s: &str) -> error::Result<u64> {
|
||||||
if let Ok(val) = humantime::parse_duration(s) {
|
if let Ok(val) = humantime::parse_duration(s) {
|
||||||
Ok(val.as_millis().try_into()?)
|
Ok(val
|
||||||
|
.as_millis()
|
||||||
|
.try_into()
|
||||||
|
.map_err(|err| BottomError::GenericError(format!("could not parse duration, {err}")))?)
|
||||||
} else if let Ok(val) = s.parse::<u64>() {
|
} else if let Ok(val) = s.parse::<u64>() {
|
||||||
Ok(val)
|
Ok(val)
|
||||||
} else {
|
} else {
|
||||||
@ -784,8 +787,10 @@ fn get_ignore_list(ignore_list: &Option<IgnoreList>) -> error::Result<Option<Fil
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let list = list.map_err(|err| BottomError::ConfigError(err.to_string()))?;
|
||||||
|
|
||||||
Ok(Some(Filter {
|
Ok(Some(Filter {
|
||||||
list: list?,
|
list,
|
||||||
is_list_ignored: ignore_list.is_list_ignored,
|
is_list_ignored: ignore_list.is_list_ignored,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{borrow::Cow, result};
|
use std::result;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
@ -17,10 +17,6 @@ pub enum BottomError {
|
|||||||
/// An error to represent generic errors.
|
/// An error to represent generic errors.
|
||||||
#[error("Error, {0}")]
|
#[error("Error, {0}")]
|
||||||
GenericError(String),
|
GenericError(String),
|
||||||
#[cfg(feature = "fern")]
|
|
||||||
/// An error to represent errors with fern.
|
|
||||||
#[error("Fern error, {0}")]
|
|
||||||
FernError(String),
|
|
||||||
/// An error to represent invalid command-line arguments.
|
/// An error to represent invalid command-line arguments.
|
||||||
#[error("Invalid argument, {0}")]
|
#[error("Invalid argument, {0}")]
|
||||||
ArgumentError(String),
|
ArgumentError(String),
|
||||||
@ -30,11 +26,6 @@ pub enum BottomError {
|
|||||||
/// An error to represent errors with converting between data types.
|
/// An error to represent errors with converting between data types.
|
||||||
#[error("Conversion error, {0}")]
|
#[error("Conversion error, {0}")]
|
||||||
ConversionError(String),
|
ConversionError(String),
|
||||||
/// An error to represent errors with a query.
|
|
||||||
#[error("Query error, {0}")]
|
|
||||||
QueryError(Cow<'static, str>),
|
|
||||||
#[error("Error casting integers {0}")]
|
|
||||||
TryFromIntError(#[from] std::num::TryFromIntError),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::io::Error> for BottomError {
|
impl From<std::io::Error> for BottomError {
|
||||||
@ -61,13 +52,6 @@ impl From<toml_edit::de::Error> for BottomError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fern")]
|
|
||||||
impl From<fern::InitError> for BottomError {
|
|
||||||
fn from(err: fern::InitError) -> Self {
|
|
||||||
BottomError::FernError(err.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<std::str::Utf8Error> for BottomError {
|
impl From<std::str::Utf8Error> for BottomError {
|
||||||
fn from(err: std::str::Utf8Error) -> Self {
|
fn from(err: std::str::Utf8Error) -> Self {
|
||||||
BottomError::ConversionError(err.to_string())
|
BottomError::ConversionError(err.to_string())
|
||||||
@ -79,13 +63,3 @@ impl From<std::string::FromUtf8Error> for BottomError {
|
|||||||
BottomError::ConversionError(err.to_string())
|
BottomError::ConversionError(err.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<regex::Error> for BottomError {
|
|
||||||
fn from(err: regex::Error) -> Self {
|
|
||||||
// We only really want the last part of it... so we'll do it the ugly way:
|
|
||||||
let err_str = err.to_string();
|
|
||||||
let error = err_str.split('\n').map(|s| s.trim()).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
BottomError::QueryError(format!("Regex error: {}", error.last().unwrap_or(&"")).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -7,7 +7,7 @@ pub static OFFSET: OnceLock<time::UtcOffset> = OnceLock::new();
|
|||||||
#[cfg(feature = "logging")]
|
#[cfg(feature = "logging")]
|
||||||
pub fn init_logger(
|
pub fn init_logger(
|
||||||
min_level: log::LevelFilter, debug_file_name: Option<&std::ffi::OsStr>,
|
min_level: log::LevelFilter, debug_file_name: Option<&std::ffi::OsStr>,
|
||||||
) -> Result<(), fern::InitError> {
|
) -> anyhow::Result<()> {
|
||||||
let dispatch = fern::Dispatch::new()
|
let dispatch = fern::Dispatch::new()
|
||||||
.format(|out, message, record| {
|
.format(|out, message, record| {
|
||||||
let offset = OFFSET.get_or_init(|| {
|
let offset = OFFSET.get_or_init(|| {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user