Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package pik for openSUSE:Factory checked in at 2024-11-09 20:58:41 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/pik (Old) and /work/SRC/openSUSE:Factory/.pik.new.2017 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "pik" Sat Nov 9 20:58:41 2024 rev:7 rq:1222972 version:0.10.0 Changes: -------- --- /work/SRC/openSUSE:Factory/pik/pik.changes 2024-10-09 22:13:58.948844311 +0200 +++ /work/SRC/openSUSE:Factory/.pik.new.2017/pik.changes 2024-11-09 20:59:29.413213746 +0100 @@ -1,0 +2,9 @@ +Sat Nov 9 13:47:41 UTC 2024 - Muhammad Akbar Yanuar Mantari <[email protected]> + +- Update to version 0.10.0 + + Bugs fixes + - Fixed empty query for ports args and paths + + New Features + - Added sorting search results by match score + +------------------------------------------------------------------- Old: ---- pik-0.9.0.tar.gz New: ---- pik-0.10.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ pik.spec ++++++ --- /var/tmp/diff_new_pack.TzskwY/_old 2024-11-09 20:59:29.985237541 +0100 +++ /var/tmp/diff_new_pack.TzskwY/_new 2024-11-09 20:59:29.985237541 +0100 @@ -18,7 +18,7 @@ %bcond_without test Name: pik -Version: 0.9.0 +Version: 0.10.0 Release: 0 Summary: Process Interactive Kill License: MIT ++++++ pik-0.9.0.tar.gz -> pik-0.10.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pik-0.9.0/Cargo.lock new/pik-0.10.0/Cargo.lock --- old/pik-0.9.0/Cargo.lock 2024-10-09 11:13:08.000000000 +0200 +++ new/pik-0.10.0/Cargo.lock 2024-11-09 14:32:43.000000000 +0100 @@ -592,7 +592,7 @@ [[package]] name = "pik" -version = "0.9.0" +version = "0.10.0" dependencies = [ "anyhow", "chrono", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pik-0.9.0/Cargo.toml new/pik-0.10.0/Cargo.toml --- old/pik-0.9.0/Cargo.toml 2024-10-09 11:13:08.000000000 +0200 +++ new/pik-0.10.0/Cargo.toml 2024-11-09 14:32:43.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "pik" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = ["Jacek Kurlit"] keywords = ["terminal", "process", "linux", "system", "kill"] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pik-0.9.0/pik.spec new/pik-0.10.0/pik.spec --- old/pik-0.9.0/pik.spec 2024-10-09 11:13:08.000000000 +0200 +++ new/pik-0.10.0/pik.spec 2024-11-09 14:32:43.000000000 +0100 @@ -1,5 +1,5 @@ Name: pik -Version: 0.9.0 +Version: 0.10.0 Release: 1%{?dist} License: MIT Summary: Process Interactive Kill is a tool that helps to find and kill process diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pik-0.9.0/src/processes/filters.rs new/pik-0.10.0/src/processes/filters.rs --- old/pik-0.9.0/src/processes/filters.rs 2024-10-09 11:13:08.000000000 +0200 +++ new/pik-0.10.0/src/processes/filters.rs 2024-11-09 14:32:43.000000000 +0100 @@ -1,7 +1,7 @@ use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; use sysinfo::Uid; -use super::{utils::get_process_args, ProcessInfo}; +use super::{utils::get_process_args, MatchData, ProcessInfo}; pub(super) struct QueryFilter { query: String, @@ -40,8 +40,7 @@ matcher, } } - - pub(super) fn accept(&self, prc: &impl ProcessInfo, ports: Option<&str>) -> bool { + pub(super) fn accept(&self, prc: &impl ProcessInfo, ports: Option<&str>) -> MatchData { match self.search_by { SearchBy::Cmd => self.query_match_str(prc.cmd()), SearchBy::Path => self.query_matches_opt(prc.cmd_path()), @@ -50,39 +49,74 @@ SearchBy::Pid => self.query_eq_u32(prc.pid()), SearchBy::ProcessFamily => self.query_matches_process_family(prc), SearchBy::Everywhere => { - self.query_match_str(prc.cmd()) - || self.query_matches_opt(prc.cmd_path()) - || self.query_matches_opt(ports) - || self.query_contains_vec(get_process_args(prc)) + let matched = self.query_match_str(prc.cmd()); + if matched.positive_match() { + return matched; + } + let matched = self.query_matches_opt(prc.cmd_path()); + if matched.positive_match() { + return matched; + } + let matched = self.query_matches_opt(ports); + if matched.positive_match() { + return matched; + } + let matched = self.query_contains_vec(get_process_args(prc)); + if matched.positive_match() { + return matched; + } + MatchData::none() } - SearchBy::None => true, + SearchBy::None => MatchData::perfect(), } } - fn query_match_str(&self, s: &str) -> bool { - let score = self.matcher.fuzzy_match(s, self.query.as_str()); - // TODO: fine-tune the score threshold or make it configurable? - score.map(|s| s >= 0).unwrap_or(false) + fn query_match_str(&self, s: &str) -> MatchData { + if self.query.is_empty() { + return MatchData::perfect(); + } + match self.matcher.fuzzy_match(s, self.query.as_str()) { + Some(score) => MatchData::new(score), + None => MatchData::none(), + } } - fn query_matches_opt(&self, s: Option<&str>) -> bool { - s.map(|s| self.query_match_str(s)).unwrap_or(false) + fn query_matches_opt(&self, s: Option<&str>) -> MatchData { + s.map(|s| self.query_match_str(s)) + .unwrap_or(MatchData::none()) } - fn query_contains_vec(&self, s: Vec<&str>) -> bool { - s.iter().any(|a| a.to_lowercase().contains(&self.query)) + fn query_contains_vec(&self, items: Vec<&str>) -> MatchData { + if items + .iter() + .any(|item| item.to_lowercase().contains(&self.query)) + { + MatchData::perfect() + } else { + MatchData::none() + } } - fn query_eq_u32(&self, s: u32) -> bool { - s.to_string() == self.query + fn query_eq_u32(&self, s: u32) -> MatchData { + if s.to_string() == self.query { + MatchData::perfect() + } else { + MatchData::none() + } } - fn query_matches_process_family(&self, prc: &impl ProcessInfo) -> bool { - self.query_eq_u32(prc.pid()) - || prc - .parent_id() - .map(|pid| self.query_eq_u32(pid)) - .unwrap_or(false) + fn query_matches_process_family(&self, prc: &impl ProcessInfo) -> MatchData { + if prc.pid().to_string() == self.query { + return MatchData::perfect(); + } + if prc + .parent_id() + .map(|pid| pid.to_string() == self.query) + .unwrap_or(false) + { + return MatchData::perfect(); + } + MatchData::none() } } @@ -179,22 +213,22 @@ cmd: "TeSt".to_string(), ..Default::default() }; - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd = "test".to_string(); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd = "TEST".to_string(); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd = "Testificator".to_string(); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd = "online_TESTER".to_string(); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd = "xxx".to_string(); - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -204,26 +238,33 @@ cmd_path: Some("/TeSt".to_string()), ..Default::default() }; - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); // tests that fuzzy search works process.cmd_path = Some("/taest".to_string()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd_path = Some("/test".to_string()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd_path = Some("/TEST".to_string()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd_path = Some("/testing_dir".to_string()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd_path = Some("/cargo/tests".to_string()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); + + process.cmd_path = Some("/xxx".to_string()); + assert!(filter.accept(&process, None).negative_match()); + // '/' accepts all non empty paths + let filter = QueryFilter::new("/"); process.cmd_path = Some("/xxx".to_string()); - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); + process.cmd_path = None; + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -232,22 +273,29 @@ let mut process = MockProcessInfo::default(); process = process.with_args(&["-TeSt"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["-test"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["-TEST"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["arg1, arg2, --testifier"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["testimony"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["-xxx"]); - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); + + // '-' accepts all non empty args + let filter = QueryFilter::new("-"); + process = process.with_args(&["-arg"]); + assert!(filter.accept(&process, None).positive_match()); + process = process.with_args(&[]); + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -258,7 +306,7 @@ args: vec!["-test".into(), "-xxx".into()], ..Default::default() }; - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -266,15 +314,22 @@ let filter = QueryFilter::new(":12"); let process = MockProcessInfo::default(); - assert!(filter.accept(&process, Some("1234"))); + assert!(filter.accept(&process, Some("1234")).positive_match()); + + assert!(filter.accept(&process, Some("3312")).positive_match()); - assert!(filter.accept(&process, Some("3312"))); + assert!(filter.accept(&process, Some("5125")).positive_match()); - assert!(filter.accept(&process, Some("5125"))); + assert!(filter + .accept(&process, Some("1111, 2222, 1234")) + .positive_match()); - assert!(filter.accept(&process, Some("1111, 2222, 1234"))); + assert!(filter.accept(&process, Some("7777")).negative_match()); - assert!(!filter.accept(&process, Some("7777"))); + //':' accepts all non empty ports + let filter = QueryFilter::new(":"); + assert!(filter.accept(&process, Some("5125")).positive_match()); + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -285,9 +340,9 @@ ..Default::default() }; - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.pid = 12345; - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -298,16 +353,16 @@ ..Default::default() }; - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.pid = 555; - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); process.parent_pid = Some(1234); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.parent_pid = Some(555); - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); process.parent_pid = None; - assert!(!filter.accept(&process, None)); + assert!(filter.accept(&process, None).negative_match()); } #[test] @@ -317,39 +372,39 @@ cmd: "TEST".into(), ..Default::default() }; - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd_path = Some("/tEsT".into()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["-TeSt"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); filter = QueryFilter::new("~80"); - assert!(filter.accept(&process, Some("8080"))); + assert!(filter.accept(&process, Some("8080")).positive_match()); process.cmd = "xxx".into(); process.cmd_path = Some("/xxx".into()); process = process.with_args(&["-xxx"]); - assert!(!filter.accept(&process, Some("1234"))); + assert!(filter.accept(&process, Some("1234")).negative_match()); } #[test] fn query_filter_search_by_none() { let filter = QueryFilter::new(""); let mut process = MockProcessInfo::default(); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd = "TeSt".to_string(); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process.cmd_path = Some("/TeSt".to_string()); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); process = process.with_args(&["-TeSt"]); - assert!(filter.accept(&process, None)); + assert!(filter.accept(&process, None).positive_match()); - assert!(filter.accept(&process, Some("1234"))); + assert!(filter.accept(&process, Some("1234")).positive_match()); } #[test] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pik-0.9.0/src/processes.rs new/pik-0.10.0/src/processes.rs --- old/pik-0.9.0/src/processes.rs 2024-10-09 11:13:08.000000000 +0200 +++ new/pik-0.10.0/src/processes.rs 2024-11-09 14:32:43.000000000 +0100 @@ -94,7 +94,7 @@ #[derive(Debug)] pub struct ProcessSearchResults { pub search_by: SearchBy, - items: Vec<Process>, + pub items: Vec<ResultItem>, } impl ProcessSearchResults { @@ -115,15 +115,15 @@ pub fn nth(&self, index: Option<usize>) -> Option<&Process> { let index = index?; - self.items.get(index) + self.items.get(index).map(|item| &item.process) } pub fn remove(&mut self, pid: u32) { - self.items.retain(|prc| prc.pid != pid) + self.items.retain(|item| item.process.pid != pid) } pub fn iter(&self) -> impl Iterator<Item = &Process> { - self.items.iter() + self.items.iter().map(|i| &i.process) } } @@ -147,20 +147,28 @@ let process_filter = QueryFilter::new(query); let options_filter = OptionsFilter::new(options, &self.current_user_id); - let items = self + let mut items = self .sys .processes() .values() .filter_map(|prc| { let ports = self.process_ports.get(&prc.pid().as_u32()); - if !options_filter.accept(prc) - || !process_filter.accept(prc, ports.map(|p| p.as_str())) - { + if !options_filter.accept(prc) { return None; } - Some(self.create_process_info(prc, ports)) + + let match_data = process_filter.accept(prc, ports.map(|p| p.as_str())); + if match_data.negative_match() { + return None; + } + Some(ResultItem::new( + match_data, + self.create_process_info(prc, ports), + )) }) - .collect(); + .collect::<Vec<ResultItem>>(); + + items.sort_by(|a, b| b.match_data.score.cmp(&a.match_data.score)); ProcessSearchResults { search_by: process_filter.search_by, @@ -275,3 +283,45 @@ .unwrap_or_default() } } + +#[derive(Debug)] +pub struct ResultItem { + pub match_data: MatchData, + pub process: Process, +} + +impl ResultItem { + pub fn new(match_data: MatchData, process: Process) -> Self { + Self { + match_data, + process, + } + } +} + +#[derive(PartialEq, Eq, Debug)] +pub struct MatchData { + pub score: i64, +} + +impl MatchData { + pub fn new(score: i64) -> Self { + Self { score } + } + + pub fn perfect() -> Self { + Self { score: i64::MAX } + } + + pub fn none() -> Self { + Self { score: -1 } + } + + pub fn positive_match(&self) -> bool { + self.score > 0 + } + + pub fn negative_match(&self) -> bool { + self.score <= 0 + } +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pik-0.9.0/tests/processes_search.rs new/pik-0.10.0/tests/processes_search.rs --- old/pik-0.9.0/tests/processes_search.rs 2024-10-09 11:13:08.000000000 +0200 +++ new/pik-0.10.0/tests/processes_search.rs 2024-11-09 14:32:43.000000000 +0100 @@ -1,6 +1,6 @@ use std::{thread, time::Duration}; -use pik::processes::{FilterOptions, ProcessManager}; +use pik::processes::{FilterOptions, ProcessManager, ProcessSearchResults}; use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher}; @@ -10,6 +10,7 @@ let results = process_manager.find_processes("cargo", FilterOptions::default()); assert!(!results.is_empty()); assert!(results.iter().all(|p| fuzzy_matches(&p.cmd, "cargo"))); + assert!(results_are_sorted_by_score(results)); } #[test] @@ -20,6 +21,7 @@ assert!(results .iter() .all(|p| fuzzy_matches(p.cmd_path.as_ref().unwrap(), "cargo"))); + assert!(results_are_sorted_by_score(results)); } #[test] @@ -32,6 +34,7 @@ .all(|p| fuzzy_matches(p.cmd_path.as_ref().unwrap(), "cargo") || p.args.contains("cargo") || fuzzy_matches(&p.cmd, "cargo"))); + assert!(results_are_sorted_by_score(results)); } #[test] @@ -40,6 +43,7 @@ let results = process_manager.find_processes("-test", FilterOptions::default()); assert!(!results.is_empty()); assert!(results.iter().all(|p| fuzzy_matches(&p.args, "test"))); + assert!(results_are_sorted_by_score(results)); } use http_test_server::TestServer; @@ -53,6 +57,7 @@ let results = process_manager.find_processes(&format!(":{}", port), FilterOptions::default()); assert!(!results.is_empty()); assert!(results.iter().all(|p| p.ports == Some(format!("{}", port)))); + assert!(results_are_sorted_by_score(results)); } #[test] @@ -65,6 +70,7 @@ .find_processes(&format!("!{}", cargo_process_pid), FilterOptions::default()); assert_eq!(restults.len(), 1); assert_eq!(restults.nth(Some(0)).unwrap().pid, cargo_process_pid); + assert!(results_are_sorted_by_score(results)); } #[test] @@ -79,6 +85,7 @@ assert!(results .iter() .all(|p| p.pid == cargo_process_pid || p.parent_pid == Some(cargo_process_pid))); + assert!(results_are_sorted_by_score(results)); } fn fuzzy_matches(value: &str, pattern: &str) -> bool { @@ -87,3 +94,11 @@ .unwrap_or(0) > 0 } + +fn results_are_sorted_by_score(results: ProcessSearchResults) -> bool { + results + .items + .iter() + .zip(results.items.iter().skip(1)) + .all(|(a, b)| a.match_data.score >= b.match_data.score) +}
