support custom cache file, fixes 26

This commit is contained in:
Alexander Mohr 2025-06-08 20:15:01 +02:00
parent 184c9a2b77
commit 74594d688d
7 changed files with 68 additions and 79 deletions

View file

@ -5,6 +5,7 @@ use hyprland::{
}; };
use rayon::prelude::*; use rayon::prelude::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf;
use std::{env, fs, sync::Arc, thread}; use std::{env, fs, sync::Arc, thread};
use sysinfo::{Pid, System}; use sysinfo::{Pid, System};
use worf::{ use worf::{
@ -115,7 +116,7 @@ impl ItemProvider<Window> for WindowProvider {
} }
} }
fn load_icon_cache(cache_path: &String) -> Result<HashMap<String, String>, Error> { fn load_icon_cache(cache_path: &PathBuf) -> Result<HashMap<String, String>, Error> {
let toml_content = let toml_content =
fs::read_to_string(cache_path).map_err(|e| Error::UpdateCacheError(format!("{e}")))?; fs::read_to_string(cache_path).map_err(|e| Error::UpdateCacheError(format!("{e}")))?;
let cache: HashMap<String, String> = toml::from_str(&toml_content) let cache: HashMap<String, String> = toml::from_str(&toml_content)
@ -123,15 +124,6 @@ fn load_icon_cache(cache_path: &String) -> Result<HashMap<String, String>, Error
Ok(cache) Ok(cache)
} }
fn cache_path() -> Result<String, Error> {
let path = dirs::cache_dir()
.map(|x| x.join("worf-hyprswitch"))
.ok_or_else(|| Error::UpdateCacheError("cannot read cache file".to_owned()))?;
desktop::create_file_if_not_exists(&path)?;
Ok(path.to_string_lossy().into_owned())
}
fn main() -> Result<(), String> { fn main() -> Result<(), String> {
env_logger::Builder::new() env_logger::Builder::new()
.parse_filters(&env::var("RUST_LOG").unwrap_or_else(|_| "error".to_owned())) .parse_filters(&env::var("RUST_LOG").unwrap_or_else(|_| "error".to_owned()))
@ -141,7 +133,8 @@ fn main() -> Result<(), String> {
let args = config::parse_args(); let args = config::parse_args();
let config = config::load_config(Some(&args)).unwrap_or(args); let config = config::load_config(Some(&args)).unwrap_or(args);
let cache_path = cache_path().map_err(|err| err.to_string())?; let cache_path =
desktop::cache_file_path(&config, "worf-hyprswitch").map_err(|err| err.to_string())?;
let mut cache = load_icon_cache(&cache_path).map_err(|e| e.to_string())?; let mut cache = load_icon_cache(&cache_path).map_err(|e| e.to_string())?;
let provider = WindowProvider::new(&config, &cache)?; let provider = WindowProvider::new(&config, &cache)?;

View file

@ -245,7 +245,7 @@ pub struct Config {
allow_markup: Option<bool>, allow_markup: Option<bool>,
#[clap(short = 'k', long = "cache-file")] #[clap(short = 'k', long = "cache-file")]
cache_file: Option<String>, // todo support this cache_file: Option<String>,
/// Defines which terminal to use. defaults to the first one found: /// Defines which terminal to use. defaults to the first one found:
/// * kitty /// * kitty
@ -291,6 +291,7 @@ pub struct Config {
)] )]
location: Option<Vec<Anchor>>, location: Option<Vec<Anchor>>,
/// If set to `true` sub actions will be disabled
#[clap(short = 'a', long = "no-actions")] #[clap(short = 'a', long = "no-actions")]
no_actions: Option<bool>, no_actions: Option<bool>,
@ -621,6 +622,11 @@ impl Config {
self.allow_markup.unwrap_or(false) self.allow_markup.unwrap_or(false)
} }
#[must_use]
pub fn cache_file(&self) -> Option<String> {
self.cache_file.clone()
}
#[must_use] #[must_use]
pub fn password(&self) -> Option<String> { pub fn password(&self) -> Option<String> {
self.password.clone() self.password.clone()

View file

@ -219,6 +219,22 @@ fn start_forked_cmd(mut cmd: Command) -> Result<(), Error> {
Ok(()) Ok(())
} }
/// Get the path of a given cache file
/// # Errors
/// Will return Error if the cache file cannot be created or not found.
pub fn cache_file_path(config: &Config, name: &str) -> Result<PathBuf, Error> {
let path = if let Some(cfg) = config.cache_file() {
PathBuf::from(cfg)
} else {
dirs::cache_dir()
.map(|x| x.join(name))
.ok_or_else(|| Error::UpdateCacheError("cannot read cache file".to_owned()))?
};
create_file_if_not_exists(&path)?;
Ok(path)
}
/// Parse a simple toml cache file from the format below /// Parse a simple toml cache file from the format below
/// "Key"=score /// "Key"=score
/// i.e. /// i.e.
@ -227,13 +243,9 @@ fn start_forked_cmd(mut cmd: Command) -> Result<(), Error> {
/// "Files"=50 /// "Files"=50
/// # Errors /// # Errors
/// Returns an Error when the given file is not found or did not parse. /// Returns an Error when the given file is not found or did not parse.
pub fn load_cache_file(cache_path: Option<&PathBuf>) -> Result<HashMap<String, i64>, Error> { pub fn load_cache_file(cache_path: &PathBuf) -> Result<HashMap<String, i64>, Error> {
let Some(path) = cache_path else {
return Err(Error::MissingFile);
};
let toml_content = let toml_content =
fs::read_to_string(path).map_err(|e| Error::UpdateCacheError(format!("{e}")))?; fs::read_to_string(cache_path).map_err(|e| Error::UpdateCacheError(format!("{e}")))?;
let parsed: toml::Value = toml_content let parsed: toml::Value = toml_content
.parse() .parse()
.map_err(|_| Error::ParsingError("failed to parse cache".to_owned()))?; .map_err(|_| Error::ParsingError("failed to parse cache".to_owned()))?;

View file

@ -37,12 +37,7 @@ struct AutoItemProvider {
impl AutoItemProvider { impl AutoItemProvider {
fn new(config: &Config) -> Self { fn new(config: &Config) -> Self {
AutoItemProvider { AutoItemProvider {
drun: DRunProvider::new( drun: DRunProvider::new(AutoRunType::DRun, config),
AutoRunType::DRun,
config.no_actions(),
config.sort_order(),
config.term(),
),
file: FileItemProvider::new(AutoRunType::File, config.sort_order()), file: FileItemProvider::new(AutoRunType::File, config.sort_order()),
math: MathProvider::new(AutoRunType::Math), math: MathProvider::new(AutoRunType::Math),
ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()), ssh: SshProvider::new(AutoRunType::Ssh, &config.sort_order()),
@ -157,7 +152,7 @@ pub fn show(config: &Config) -> Result<(), Error> {
provider.math.elements.push(selection_result); provider.math.elements.push(selection_result);
} }
AutoRunType::DRun => { AutoRunType::DRun => {
update_drun_cache_and_run(cache_path, &mut cache, selection_result)?; update_drun_cache_and_run(&cache_path, &mut cache, selection_result)?;
break; break;
} }
AutoRunType::File => { AutoRunType::File => {

View file

@ -28,7 +28,7 @@ struct DRunCache {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct DRunProvider<T: Clone> { pub(crate) struct DRunProvider<T: Clone> {
items: Option<Vec<MenuItem<T>>>, items: Option<Vec<MenuItem<T>>>,
pub(crate) cache_path: Option<PathBuf>, pub(crate) cache_path: PathBuf,
pub(crate) cache: HashMap<String, i64>, pub(crate) cache: HashMap<String, i64>,
data: T, data: T,
no_actions: bool, no_actions: bool,
@ -50,21 +50,16 @@ impl<T: Clone + Send + Sync> ItemProvider<T> for DRunProvider<T> {
} }
impl<T: Clone + Send + Sync> DRunProvider<T> { impl<T: Clone + Send + Sync> DRunProvider<T> {
pub(crate) fn new( pub(crate) fn new(menu_item_data: T, config: &Config) -> Self {
menu_item_data: T, let (cache_path, d_run_cache) = load_cache("drun_cache", config).unwrap();
no_actions: bool,
sort_order: SortOrder,
terminal: Option<String>,
) -> Self {
let (cache_path, d_run_cache) = load_d_run_cache();
DRunProvider { DRunProvider {
items: None, items: None,
cache_path, cache_path,
cache: d_run_cache, cache: d_run_cache,
data: menu_item_data, data: menu_item_data,
no_actions, no_actions: config.no_actions(),
sort_order, sort_order: config.sort_order(),
terminal, terminal: config.term(),
} }
} }
@ -195,21 +190,14 @@ impl<T: Clone + Send + Sync> DRunProvider<T> {
} }
} }
fn load_d_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
let cache_path = dirs::cache_dir().map(|x| x.join("worf-drun"));
load_cache(cache_path)
}
pub(crate) fn update_drun_cache_and_run<T: Clone>( pub(crate) fn update_drun_cache_and_run<T: Clone>(
cache_path: Option<PathBuf>, cache_path: &PathBuf,
cache: &mut HashMap<String, i64>, cache: &mut HashMap<String, i64>,
selection_result: MenuItem<T>, selection_result: MenuItem<T>,
) -> Result<(), Error> { ) -> Result<(), crate::Error> {
if let Some(cache_path) = cache_path { *cache.entry(selection_result.label).or_insert(0) += 1;
*cache.entry(selection_result.label).or_insert(0) += 1; if let Err(e) = save_cache_file(cache_path, cache) {
if let Err(e) = save_cache_file(&cache_path, cache) { log::warn!("cannot save drun cache {e:?}");
log::warn!("cannot save drun cache {e:?}");
}
} }
if let Some(action) = selection_result.action { if let Some(action) = selection_result.action {
@ -224,14 +212,14 @@ pub(crate) fn update_drun_cache_and_run<T: Clone>(
/// ///
/// Will return `Err` if it was not able to spawn the process /// Will return `Err` if it was not able to spawn the process
pub fn show(config: &Config) -> Result<(), Error> { pub fn show(config: &Config) -> Result<(), Error> {
let provider = DRunProvider::new(0, config.no_actions(), config.sort_order(), config.term()); let provider = DRunProvider::new(0, config);
let cache_path = provider.cache_path.clone(); let cache_path = provider.cache_path.clone();
let mut cache = provider.cache.clone(); let mut cache = provider.cache.clone();
// todo ues a arc instead of cloning the config // todo ues a arc instead of cloning the config
let selection_result = gui::show(config.clone(), provider, false, None, None); let selection_result = gui::show(config.clone(), provider, false, None, None);
match selection_result { match selection_result {
Ok(s) => update_drun_cache_and_run(cache_path, &mut cache, s.menu)?, Ok(s) => update_drun_cache_and_run(&cache_path, &mut cache, s.menu)?,
Err(_) => { Err(_) => {
log::error!("No item selected"); log::error!("No item selected");
} }

View file

@ -1,7 +1,8 @@
use crate::Error;
use crate::config::Config;
use crate::desktop::{cache_file_path, create_file_if_not_exists, load_cache_file};
use std::{collections::HashMap, path::PathBuf}; use std::{collections::HashMap, path::PathBuf};
use crate::desktop::{create_file_if_not_exists, load_cache_file};
pub mod auto; pub mod auto;
pub mod dmenu; pub mod dmenu;
pub mod drun; pub mod drun;
@ -12,15 +13,17 @@ pub mod run;
pub mod search; pub mod search;
pub mod ssh; pub mod ssh;
pub(crate) fn load_cache(cache_path: Option<PathBuf>) -> (Option<PathBuf>, HashMap<String, i64>) { pub(crate) fn load_cache(
name: &str,
config: &Config,
) -> Result<(PathBuf, HashMap<String, i64>), Error> {
let cache_path = cache_file_path(config, name)?;
let cache = { let cache = {
if let Some(ref cache_path) = cache_path { if let Err(e) = create_file_if_not_exists(&cache_path) {
if let Err(e) = create_file_if_not_exists(cache_path) { log::warn!("No drun cache file and cannot create: {e:?}");
log::warn!("No drun cache file and cannot create: {e:?}");
}
} }
load_cache_file(cache_path.as_ref()).unwrap_or_default() load_cache_file(&cache_path).unwrap_or_default()
}; };
(cache_path, cache) Ok((cache_path, cache))
} }

View file

@ -30,20 +30,20 @@ impl ItemProvider<i32> for RunProvider {
#[derive(Clone)] #[derive(Clone)]
struct RunProvider { struct RunProvider {
items: Option<Vec<MenuItem<i32>>>, items: Option<Vec<MenuItem<i32>>>,
cache_path: Option<PathBuf>, cache_path: PathBuf,
cache: HashMap<String, i64>, cache: HashMap<String, i64>,
sort_order: SortOrder, sort_order: SortOrder,
} }
impl RunProvider { impl RunProvider {
fn new(sort_order: SortOrder) -> Self { fn new(config: &Config) -> Result<Self, Error> {
let (cache_path, d_run_cache) = load_run_cache(); let (cache_path, d_run_cache) = load_cache("worf-run", config)?;
RunProvider { Ok(RunProvider {
items: None, items: None,
cache_path, cache_path,
cache: d_run_cache, cache: d_run_cache,
sort_order, sort_order: config.sort_order(),
} })
} }
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
@ -98,21 +98,14 @@ impl RunProvider {
} }
} }
fn load_run_cache() -> (Option<PathBuf>, HashMap<String, i64>) {
let cache_path = dirs::cache_dir().map(|x| x.join("worf-run"));
load_cache(cache_path)
}
fn update_run_cache_and_run<T: Clone>( fn update_run_cache_and_run<T: Clone>(
cache_path: Option<PathBuf>, cache_path: &PathBuf,
cache: &mut HashMap<String, i64>, cache: &mut HashMap<String, i64>,
selection_result: MenuItem<T>, selection_result: MenuItem<T>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if let Some(cache_path) = cache_path { *cache.entry(selection_result.label).or_insert(0) += 1;
*cache.entry(selection_result.label).or_insert(0) += 1; if let Err(e) = save_cache_file(cache_path, cache) {
if let Err(e) = save_cache_file(&cache_path, cache) { log::warn!("cannot save run cache {e:?}");
log::warn!("cannot save run cache {e:?}");
}
} }
if let Some(action) = selection_result.action { if let Some(action) = selection_result.action {
@ -132,13 +125,12 @@ fn update_run_cache_and_run<T: Clone>(
/// ///
/// Will return `Err` if it was not able to spawn the process /// Will return `Err` if it was not able to spawn the process
pub fn show(config: &Config) -> Result<(), Error> { pub fn show(config: &Config) -> Result<(), Error> {
let provider = RunProvider::new(config.sort_order()); let provider = RunProvider::new(config)?;
let cache_path = provider.cache_path.clone(); let cache_path = provider.cache_path.clone();
let mut cache = provider.cache.clone(); let mut cache = provider.cache.clone();
let selection_result = gui::show(config.clone(), provider, false, None, None); let selection_result = gui::show(config.clone(), provider, false, None, None);
match selection_result { match selection_result {
Ok(s) => update_run_cache_and_run(cache_path, &mut cache, s.menu)?, Ok(s) => update_run_cache_and_run(&cache_path, &mut cache, s.menu)?,
Err(_) => { Err(_) => {
log::error!("No item selected"); log::error!("No item selected");
} }