diff --git a/Cargo.lock b/Cargo.lock index 5e947b4..da584e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -48,6 +48,12 @@ dependencies = [ "target-lexicon", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" @@ -399,6 +405,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "libc" version = "0.2.153" @@ -425,6 +440,7 @@ name = "oreon-system-manager" version = "0.1.0" dependencies = [ "gtk4", + "itertools", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0494824..742bcb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] gtk = { version = "0.9.0", package = "gtk4" } +itertools = "0.13.0" diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index e7242cf..18d5631 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -1,6 +1,9 @@ +use std::process::Command; + use gtk::{ prelude::BoxExt, Align, Button, ListBox, Orientation, ScrolledWindow, SearchBar, SearchEntry, }; +use itertools::Itertools; pub mod objects; @@ -135,3 +138,59 @@ pub fn installed_list() -> ScrolledWindow { scrollable } + +pub fn get_driver_details() -> Vec { + let basic_driver_details = String::from_utf8( + std::process::Command::new("lspci") + .output() + .expect("Failed to run 'lspci'!") + .stdout, + ) + .expect("Failed to convert the 'lspci' output to a string."); + + let advanced_driver_details = String::from_utf8( + std::process::Command::new("lspci") + .arg("-k") + .output() + .expect("Failed to run 'lspci -k'!") + .stdout, + ) + .expect("Failed to convert the 'lspci -k' output to a string."); + + let pci_ids: Vec<&str> = basic_driver_details + .lines() + .map(|x| x.split_whitespace().next().unwrap_or("")) + .filter(|x| !x.is_empty()) + .collect(); + + let mut driver_names = vec![]; + + for i in 0..(pci_ids.len() - 1) { + driver_names.push( + advanced_driver_details + .split_once(pci_ids[i]) + .expect("Failed to find PCI ID!") + .1 + .split_once(pci_ids[i + 1]) + .expect("Failed to get next PCI ID!") + .0 + .lines() + .filter(|x| x.trim().contains("driver")) + .map(|x| { + x.split(":") + .nth(1) + .expect("Failed to find colon in driver definition.") + .trim() + }) + .collect::(), + ); + } + + driver_names + .iter() + .filter(|x| !x.is_empty()) + .map(|x| x.to_owned()) + .dedup() + .sorted() + .collect() +} diff --git a/src/main.rs b/src/main.rs index 3da447b..d9e7457 100644 --- a/src/main.rs +++ b/src/main.rs @@ -126,3 +126,14 @@ fn on_startup(_: &Application) { gtk::STYLE_PROVIDER_PRIORITY_USER, ); } + +#[cfg(test)] +mod tests { + use crate::drivers::get_driver_details; + + #[test] + fn test_drivers() { + dbg!(get_driver_details()); + panic!(); + } +} diff --git a/src/packages/browse.rs b/src/packages/browse.rs index fe8d3e3..9dd8aaf 100644 --- a/src/packages/browse.rs +++ b/src/packages/browse.rs @@ -6,6 +6,7 @@ use std::process::Command; use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry}; +pub static mut FULL_PKG_LIST: Vec = vec![]; pub static mut PACKAGES_LIST: Option = None; pub static mut PKG_LIST: Vec = vec![]; pub static mut PKG_LIST_INDEX: usize = 0; @@ -56,45 +57,43 @@ pub fn browse() -> gtk::Box { .halign(Align::Fill) .hexpand(true) .build(), + ); + + FULL_PKG_LIST = String::from_utf8( + Command::new("dnf") + .args(["list", "--available"]) + .output() + .unwrap() + .stdout, ) - }; + .unwrap() + .lines() + .map(|x| { + let y = x.to_string(); + let b = y + .split_whitespace() + .next() + .unwrap_or("") + .split(".") + .next() + .unwrap(); + b.to_string() + }) + .filter(|x| { + !x.trim().is_empty() + && !x.contains("=") + && !skip_list.contains(&x.to_lowercase().trim()) + }) + .collect(); - // TODO: Make this part asynchronous so that the app opens faster - let pkg_list = String::from_utf8( - Command::new("dnf") - .args(["list", "--available"]) - .output() - .unwrap() - .stdout, - ) - .unwrap(); + PKG_LIST = FULL_PKG_LIST.clone(); - unsafe { /* Gets the list of packages, which also needs to be static mutable for the same reason as PACKAGES_LIST, and remove the description and architecture details, and also filter out the lines that aren't actually packages. */ - PKG_LIST = pkg_list - .lines() - .map(|x| { - let y = x.to_string(); - let b = y - .split_whitespace() - .next() - .unwrap_or("") - .split(".") - .next() - .unwrap(); - b.to_string() - }) - .filter(|x| { - !x.trim().is_empty() - && !x.contains("=") - && !skip_list.contains(&x.to_lowercase().trim()) - }) - .collect(); /* Add buttons for the first 50 packages in the list, to @@ -102,7 +101,7 @@ pub fn browse() -> gtk::Box { package and a button for it in the memory at the same time, which has caused as much as 1.4GB of RAM usage during tests. */ - for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() { + for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].to_vec() { PKG_LIST_INDEX += 1; let pkg_btn = Button::builder() .label(pkg) @@ -164,7 +163,7 @@ pub fn browse() -> gtk::Box { pkgs_entry.connect_search_changed(move |x| { let f = x.clone(); - unsafe { search(&f, &skip_list) } + unsafe { search(&f) } }); pkgs_search.connect_entry(&pkgs_entry); @@ -188,44 +187,20 @@ Function that takes a search entry and updates the package lists accordingly, skipping the values stated in `skip_list` */ -unsafe fn search(f: &SearchEntry, skip_list: &Vec<&str>) { - /* - Get a list of installed packages from DNF - TODO: Make this asynchronous - */ - let pkg_list = String::from_utf8( - Command::new("dnf") - .args(["search", f.text().to_string().as_str()]) - .output() - .unwrap() - .stdout, - ) - .unwrap(); - +unsafe fn search(f: &SearchEntry) { /* Reinitialize PKG_LIST with an extra predicate in the filter closure to ensure that the list of packages contains only ones that match the query */ - PKG_LIST = pkg_list - .lines() - .map(|x| { - let y = x.to_string(); - let b = y - .split_whitespace() - .next() - .unwrap_or("") - .split(".") - .next() - .unwrap(); - b.to_string() - }) + PKG_LIST = FULL_PKG_LIST + .iter() .filter(|x| { - !x.trim().is_empty() - && !x.contains("=") - && !skip_list.contains(&x.to_lowercase().trim()) + x.to_lowercase() + .contains(f.text().to_string().to_lowercase().as_str()) }) + .map(|x| x.clone()) .collect(); /* diff --git a/src/packages/installed.rs b/src/packages/installed.rs index bd461ac..542ba48 100644 --- a/src/packages/installed.rs +++ b/src/packages/installed.rs @@ -4,10 +4,13 @@ use gtk::{ }; use std::process::Command; +use itertools::Itertools; + use gtk::{Align, ListBox, ScrolledWindow, SearchBar, SearchEntry}; use super::show_package; +pub static mut FULL_PKG_LIST: Vec = vec![]; pub static mut PACKAGES_LIST: Option = None; pub static mut PKG_LIST: Vec = vec![]; pub static mut PKG_LIST_INDEX: usize = 0; @@ -62,45 +65,41 @@ pub fn installed() -> gtk::Box { .halign(Align::Fill) .hexpand(true) .build(), - ) - }; + ); - // TODO: Make this part asynchronous so that the app opens faster - let pkg_list = String::from_utf8( - Command::new("dnf") - .args(["list", "--installed"]) - .output() - .unwrap() - .stdout, - ) - .unwrap(); - - unsafe { /* Gets the list of packages, which also needs to be static mutable for the same reason as PACKAGES_LIST, and remove the description and architecture details, and also filter out the lines that aren't actually packages. */ - PKG_LIST = pkg_list - .lines() - .map(|x| { - let y = x.to_string(); - let b = y - .split_whitespace() - .next() - .unwrap_or("") - .split(".") - .next() - .unwrap(); - b.to_string() - }) - .filter(|x| { - !x.trim().is_empty() - && !x.contains("=") - && !skip_list.contains(&x.to_lowercase().trim()) - }) - .collect(); + FULL_PKG_LIST = String::from_utf8( + Command::new("dnf") + .args(["list", "--installed"]) + .output() + .unwrap() + .stdout, + ) + .unwrap() + .lines() + .map(|x| { + let y = x.to_string(); + let b = y + .split_whitespace() + .next() + .unwrap_or("") + .split(".") + .next() + .unwrap(); + b.to_string() + }) + .filter(|x| { + !x.trim().is_empty() + && !x.contains("=") + && !skip_list.contains(&x.to_lowercase().trim()) + }) + .dedup() + .collect::>(); /* Add buttons for the first 50 packages in the list, to @@ -108,7 +107,7 @@ pub fn installed() -> gtk::Box { package and a button for it in the memory at the same time, which has caused as much as 1.4GB of RAM usage during tests. */ - for pkg in PKG_LIST[0..(50.min(PKG_LIST.len()))].to_vec() { + for pkg in FULL_PKG_LIST[0..(50.min(FULL_PKG_LIST.len()))].to_vec() { PKG_LIST_INDEX += 1; let pkg_btn = Button::builder() .label(pkg) @@ -144,9 +143,9 @@ pub fn installed() -> gtk::Box { .unwrap() .connect_edge_reached(|_, edge| { if edge == PositionType::Bottom { - for i in PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(PKG_LIST.len())) { + for i in PKG_LIST_INDEX..((PKG_LIST_INDEX + 50).min(FULL_PKG_LIST.len())) { let pkg_btn = Button::builder() - .label(PKG_LIST[i].as_str()) + .label(FULL_PKG_LIST[i].as_str()) .css_classes(["package-name"]) .build(); @@ -168,7 +167,7 @@ pub fn installed() -> gtk::Box { pkgs_entry.connect_search_changed(move |x| { let f = x.clone(); - unsafe { search(&f, &skip_list) } + unsafe { search(&f) } }); pkgs_search.connect_entry(&pkgs_entry); @@ -192,46 +191,20 @@ Function that takes a search entry and updates the package lists accordingly, skipping the values stated in `skip_list` */ -unsafe fn search(f: &SearchEntry, skip_list: &Vec<&str>) { - /* - Get a list of installed packages from DNF - TODO: Make this asynchronous - */ - let pkg_list = String::from_utf8( - Command::new("dnf") - .args(["list", "--installed"]) - .output() - .unwrap() - .stdout, - ) - .unwrap(); - +unsafe fn search(f: &SearchEntry) { /* Reinitialize PKG_LIST with an extra predicate in the filter closure to ensure that the list of packages contains only ones that match the query */ - PKG_LIST = pkg_list - .lines() - .map(|x| { - let y = x.to_string(); - let b = y - .split_whitespace() - .next() - .unwrap_or("") - .split(".") - .next() - .unwrap(); - b.to_string() - }) + PKG_LIST = FULL_PKG_LIST + .iter() .filter(|x| { - !x.trim().is_empty() - && !x.contains("=") - && !skip_list.contains(&x.to_lowercase().trim()) - && x.to_lowercase() - .contains(f.text().to_string().to_lowercase().as_str()) + x.to_lowercase() + .contains(f.text().to_string().to_lowercase().as_str()) }) + .map(|x| x.clone()) .collect(); /*