diff --git a/examples/worf-hyprspace/src/main.rs b/examples/worf-hyprspace/src/main.rs index ded3088..29a3e92 100644 --- a/examples/worf-hyprspace/src/main.rs +++ b/examples/worf-hyprspace/src/main.rs @@ -2,17 +2,15 @@ use std::{ env, fmt::{Display, Formatter}, str::FromStr, - sync::{Arc, Mutex, RwLock}, + sync::{Arc, LazyLock, Mutex, RwLock}, thread::sleep, time::{Duration, Instant}, }; use clap::Parser; -use hyprland::data::Client; -use hyprland::dispatch::WindowIdentifier; use hyprland::{ - data::{Workspace, Workspaces}, - dispatch::{Dispatch, DispatchType, WorkspaceIdentifierWithSpecial}, + data::{Client, Workspace, Workspaces}, + dispatch::{Dispatch, DispatchType, WindowIdentifier, WorkspaceIdentifierWithSpecial}, prelude::HyprData, shared::HyprDataActive, }; @@ -478,12 +476,14 @@ fn main() -> Result<(), String> { cfg.worf.set_prompt(cfg.hypr_space_mode().to_string()); } - let pattern = Mode::iter() - .map(|m| regex::escape(&m.to_string().to_lowercase())) - .collect::>() - .join("|"); - - let pattern = Regex::new(&format!("(?i){pattern}")).map_err(|e| e.to_string())?; + static PATTERN_RE: LazyLock = LazyLock::new(|| { + let pattern = Mode::iter() + .map(|m| regex::escape(&m.to_string().to_lowercase())) + .collect::>() + .join("|"); + Regex::new(&format!("(?i){pattern}")).unwrap() + }); + let pattern = PATTERN_RE.clone(); let provider = Arc::new(Mutex::new(HyprspaceProvider::new( &cfg, diff --git a/worf/src/lib/desktop.rs b/worf/src/lib/desktop.rs index 2350651..3d1c5c5 100644 --- a/worf/src/lib/desktop.rs +++ b/worf/src/lib/desktop.rs @@ -6,9 +6,11 @@ use std::{ os::unix::{fs::PermissionsExt, prelude::CommandExt}, path::{Path, PathBuf}, process::{Command, Stdio}, + sync::LazyLock, time::Instant, }; +// re-export freedesktop_file_parser for easier access pub use freedesktop_file_parser::{DesktopFile, EntryType}; use notify_rust::Notification; use rayon::prelude::*; @@ -26,8 +28,9 @@ use crate::{ /// When it cannot parse the internal regex #[must_use] pub fn known_image_extension_regex_pattern() -> Regex { - Regex::new(r"(?i).*\.(png|jpg|gif|svg|jpeg)$") - .expect("Internal image regex is not valid anymore.") + static RE: LazyLock = + LazyLock::new(|| Regex::new(r"(?i).*\.(png|jpg|gif|svg|jpeg)$").unwrap()); + RE.clone() } /// Helper function to retrieve a file with given regex. @@ -59,6 +62,8 @@ fn find_files(folder: &Path, file_name: &Regex) -> Option> { /// When it cannot parse the internal regex #[must_use] pub fn find_desktop_files() -> Vec { + static DESKTOP_RE: LazyLock = LazyLock::new(|| Regex::new(r"(?i).*\.desktop$").unwrap()); + let mut paths = vec![ PathBuf::from("/usr/share/applications"), PathBuf::from("/usr/local/share/applications"), @@ -81,12 +86,10 @@ pub fn find_desktop_files() -> Vec { } } - let regex = &Regex::new("(?i).*\\.desktop$").expect("invalid internal regex"); - let p: Vec<_> = paths .into_par_iter() .filter(|desktop_dir| desktop_dir.exists()) - .filter_map(|desktop_dir| find_files(&desktop_dir, regex)) + .filter_map(|desktop_dir| find_files(&desktop_dir, &DESKTOP_RE)) .flat_map(|desktop_files| { desktop_files.into_par_iter().filter_map(|desktop_file| { fs::read_to_string(desktop_file) @@ -160,7 +163,9 @@ pub fn fork_if_configured(config: &Config) { /// # Panics /// When internal regex unwrapping fails. Should not happen as the regex is static pub fn spawn_fork(cmd: &str, working_dir: Option<&String>) -> Result<(), Error> { - let re = Regex::new(r#"'([^']*)'|"([^"]*)"|(\S+)"#).expect("invalid regex in spawn_fork"); + static RE: LazyLock = + LazyLock::new(|| Regex::new(r#"'([^']*)'|"([^"]*)"|(\S+)"#).unwrap()); + let re = &*RE; let parts: Vec = re .captures_iter(cmd) .map(|cap| { diff --git a/worf/src/lib/modes/auto.rs b/worf/src/lib/modes/auto.rs index a9329b0..202e119 100644 --- a/worf/src/lib/modes/auto.rs +++ b/worf/src/lib/modes/auto.rs @@ -1,12 +1,14 @@ -use regex::Regex; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::{Arc, LazyLock, Mutex, RwLock}; + +use regex::Regex; -use crate::gui::ArcProvider; use crate::{ Error, config::Config, desktop::spawn_fork, - gui::{self, DefaultItemFactory, ExpandMode, ItemProvider, MenuItem, ProviderData}, + gui::{ + self, ArcProvider, DefaultItemFactory, ExpandMode, ItemProvider, MenuItem, ProviderData, + }, modes::{ drun::{DRunProvider, update_drun_cache_and_run}, file::FileItemProvider, @@ -74,15 +76,15 @@ impl AutoItemProvider { fn contains_math_functions_or_starts_with_number(input: &str) -> bool { // Regex for function names (word boundaries to match whole words) - let math_functions = r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e|0x|0b|\||&|<<|>>|\^)\b"; + static MATH_REGEX: LazyLock = LazyLock::new(|| { + Regex::new(r"\b(sqrt|abs|exp|ln|sin|cos|tan|asin|acos|atan|atan2|sinh|cosh|tanh|asinh|acosh|atanh|floor|ceil|round|signum|min|max|pi|e|0x|0b|\||&|<<|>>|\^)\b").unwrap() + }); // Regex for strings that start with a number (including decimals) - let starts_with_number = r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)"; + static NUMBER_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^\s*[+-]?(\d+(\.\d*)?|\.\d+)").unwrap()); - let math_regex = Regex::new(math_functions).unwrap(); - let number_regex = Regex::new(starts_with_number).unwrap(); - - math_regex.is_match(input) || number_regex.is_match(input) + MATH_REGEX.is_match(input) || NUMBER_REGEX.is_match(input) } impl ItemProvider for AutoItemProvider { @@ -99,7 +101,8 @@ impl ItemProvider for AutoItemProvider { } else if search.starts_with("ssh") { (AutoRunType::Ssh, self.ssh.get_elements(search_opt)) } else if search.starts_with('?') { - let re = Regex::new(r"^\?\s*").unwrap(); + static RE: LazyLock = LazyLock::new(|| Regex::new(r"^\?\s*").unwrap()); + let re = &*RE; let query = re.replace(search, ""); ( AutoRunType::WebSearch, @@ -151,7 +154,7 @@ pub fn show(config: &Arc>) -> Result<(), Error> { Some( vec!["ssh", "emoji", "^\\$\\w+", "^\\?\\s*"] .into_iter() - .map(|s| Regex::new(s).unwrap()) + .map(|s| Regex::new(s).unwrap()) // Consider precompiling if s is constant .collect(), ), ExpandMode::Verbatim, diff --git a/worf/src/lib/modes/drun.rs b/worf/src/lib/modes/drun.rs index 66f152a..f634bfa 100644 --- a/worf/src/lib/modes/drun.rs +++ b/worf/src/lib/modes/drun.rs @@ -9,7 +9,6 @@ use freedesktop_file_parser::EntryType; use rayon::prelude::*; use serde::{Deserialize, Serialize}; -use crate::gui::ArcProvider; use crate::{ Error, config::{Config, SortOrder}, @@ -17,7 +16,7 @@ use crate::{ find_desktop_files, get_locale_variants, lookup_name_with_locale, save_cache_file, spawn_fork, }, - gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData}, + gui::{self, ArcProvider, ExpandMode, ItemProvider, MenuItem, ProviderData}, modes::load_cache, }; diff --git a/worf/src/lib/modes/file.rs b/worf/src/lib/modes/file.rs index 73f847d..bb16fba 100644 --- a/worf/src/lib/modes/file.rs +++ b/worf/src/lib/modes/file.rs @@ -1,12 +1,12 @@ -use regex::Regex; -use std::sync::Mutex; use std::{ fs, os::unix::fs::FileTypeExt, path::{Path, PathBuf}, - sync::{Arc, RwLock}, + sync::{Arc, LazyLock, Mutex, RwLock}, }; +use regex::Regex; + use crate::{ Error, config::{Config, SortOrder, expand_path}, @@ -201,6 +201,8 @@ impl ItemProvider for FileItemProvider { /// # Panics /// In case an internal regex does not parse anymore, this should never happen pub fn show(config: &Arc>) -> Result<(), Error> { + static RE: LazyLock = LazyLock::new(|| Regex::new(r"^\$\w+").unwrap()); + let provider = Arc::new(Mutex::new(FileItemProvider::new( 0, config.read().unwrap().sort_order(), @@ -211,7 +213,7 @@ pub fn show(config: &Arc>) -> Result<(), Error> { config, provider, None, - Some(vec![Regex::new("^\\$\\w+").unwrap()]), + Some(vec![RE.clone()]), ExpandMode::Verbatim, None, )?; diff --git a/worf/src/lib/modes/math.rs b/worf/src/lib/modes/math.rs index 6cf69c9..7c4e1ee 100644 --- a/worf/src/lib/modes/math.rs +++ b/worf/src/lib/modes/math.rs @@ -1,6 +1,6 @@ use std::{ collections::VecDeque, - sync::{Arc, Mutex, RwLock}, + sync::{Arc, LazyLock, Mutex, RwLock}, }; use regex::Regex; @@ -79,13 +79,12 @@ enum Value { /// Normalize base literals like 0x and 0b into decimal format fn normalize_bases(expr: &str) -> String { - let hex_re = Regex::new(r"0x[0-9a-fA-F]+").unwrap(); - let expr = hex_re.replace_all(expr, |caps: ®ex::Captures| { + static HEX_RE: LazyLock = LazyLock::new(|| Regex::new(r"0x[0-9a-fA-F]+").unwrap()); + static BIN_RE: LazyLock = LazyLock::new(|| Regex::new(r"0b[01]+").unwrap()); + let expr = HEX_RE.replace_all(expr, |caps: ®ex::Captures| { i64::from_str_radix(&caps[0][2..], 16).unwrap().to_string() }); - - let bin_re = Regex::new(r"0b[01]+").unwrap(); - bin_re + BIN_RE .replace_all(&expr, |caps: ®ex::Captures| { i64::from_str_radix(&caps[0][2..], 2).unwrap().to_string() }) diff --git a/worf/src/lib/modes/run.rs b/worf/src/lib/modes/run.rs index 70ad720..0a58540 100644 --- a/worf/src/lib/modes/run.rs +++ b/worf/src/lib/modes/run.rs @@ -7,12 +7,11 @@ use std::{ sync::{Arc, Mutex, RwLock}, }; -use crate::gui::ArcProvider; use crate::{ Error, config::{Config, SortOrder}, desktop::{is_executable, save_cache_file}, - gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData}, + gui::{self, ArcProvider, ExpandMode, ItemProvider, MenuItem, ProviderData}, modes::load_cache, }; diff --git a/worf/src/lib/modes/search.rs b/worf/src/lib/modes/search.rs index 9241139..229f4a8 100644 --- a/worf/src/lib/modes/search.rs +++ b/worf/src/lib/modes/search.rs @@ -1,4 +1,5 @@ use std::sync::{Arc, Mutex, RwLock}; + use urlencoding::encode; use crate::{ diff --git a/worf/src/lib/modes/ssh.rs b/worf/src/lib/modes/ssh.rs index 4ffe2a7..779acd4 100644 --- a/worf/src/lib/modes/ssh.rs +++ b/worf/src/lib/modes/ssh.rs @@ -1,13 +1,15 @@ -use regex::Regex; -use std::fs; -use std::sync::{Arc, Mutex, RwLock}; +use std::{ + fs, + sync::{Arc, LazyLock, Mutex, RwLock}, +}; + +use regex::Regex; -use crate::gui::{ExpandMode, ProviderData}; use crate::{ Error, config::{Config, SortOrder}, desktop::spawn_fork, - gui::{self, ItemProvider, MenuItem}, + gui::{self, ExpandMode, ItemProvider, MenuItem, ProviderData}, }; #[derive(Clone)] @@ -17,7 +19,8 @@ pub(crate) struct SshProvider { impl SshProvider { pub(crate) fn new(menu_item_data: T, order: &SortOrder) -> Self { - let re = Regex::new(r"(?m)^\s*Host\s+(.+)$").unwrap(); + static RE: LazyLock = LazyLock::new(|| Regex::new(r"(?m)^\s*Host\s+(.+)$").unwrap()); + let re = &*RE; let mut items: Vec<_> = dirs::home_dir() .map(|home| home.join(".ssh").join("config")) .filter(|path| path.exists()) diff --git a/worf/src/main.rs b/worf/src/main.rs index 2a686a5..9bbdc70 100644 --- a/worf/src/main.rs +++ b/worf/src/main.rs @@ -1,10 +1,11 @@ -use clap::Parser; -use std::fmt::Display; -use std::str::FromStr; use std::{ env, + fmt::Display, + str::FromStr, sync::{Arc, RwLock}, }; + +use clap::Parser; use worf::{Error, config, desktop::fork_if_configured, modes}; #[derive(Clone, Debug)] diff --git a/worf/src/mod.rs b/worf/src/mod.rs index e46b58a..a332f3e 100644 --- a/worf/src/mod.rs +++ b/worf/src/mod.rs @@ -1,4 +1,5 @@ use std::fmt; + use thiserror::Error; /// Configuration and command line parsing