From d7d40d49d659588bac808b072cd57ab860cffeef Mon Sep 17 00:00:00 2001 From: Kitsu Date: Mon, 7 Dec 2020 00:40:05 +0300 Subject: [PATCH] Cache most used apps and use for ordering --- Cargo.lock | 1 + Cargo.toml | 1 + src/desktop.rs | 18 ++++++++++---- src/main.rs | 2 +- src/mode/apps.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 76 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3142304..03beb52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1069,6 +1069,7 @@ dependencies = [ "font-kit", "freedesktop_entry_parser", "fuse-rust", + "lazy_static", "log", "nix 0.19.1", "raqote", diff --git a/Cargo.toml b/Cargo.toml index 2e8a19b..ce3de3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ serde = { version = "1.0.117", features = ["derive"] } toml = "0.5.7" structopt = "0.3.21" either = "1.6.1" +lazy_static = "1.4.0" [profile.release] lto = true diff --git a/src/desktop.rs b/src/desktop.rs index 9de0c67..7445047 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,19 +1,23 @@ use std::fs::{self, DirEntry}; use std::path::{Path, PathBuf}; +use lazy_static::lazy_static; use xdg::BaseDirectories; +lazy_static! { + pub static ref XDG_DIRS: BaseDirectories = BaseDirectories::new().unwrap(); +} + pub struct Entry { pub name: String, + pub desktop_fname: String, pub exec: String, pub is_terminal: bool, } pub fn find_entries() -> Vec { - let xdg = BaseDirectories::new().unwrap(); - - let mut dirs = xdg.get_data_dirs(); - dirs.push(xdg.get_data_home()); + let mut dirs = XDG_DIRS.get_data_dirs(); + dirs.push(XDG_DIRS.get_data_home()); let mut entries = vec![]; traverse_dirs(&mut entries, dirs); entries.sort_unstable_by(|x, y| x.name.cmp(&y.name)); @@ -72,6 +76,12 @@ fn traverse_dir_entry(mut entries: &mut Vec, dir_entry: DirEntry) { (Some(n), Some(e)) => { entries.push(Entry { name: n.to_owned(), + desktop_fname: dir_entry_path + .file_name() + .unwrap() + .to_str() + .expect("desktop file name is not in utf-8") + .to_owned(), exec: e.to_owned(), is_terminal: main_section .attr("Terminal") diff --git a/src/main.rs b/src/main.rs index 5fab2bb..89a2b5f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,7 @@ use sctk::{ }; use structopt::{clap::ArgGroup, StructOpt}; -pub use desktop::Entry as DesktopEntry; +pub use desktop::{Entry as DesktopEntry, XDG_DIRS}; mod config; mod desktop; diff --git a/src/mode/apps.rs b/src/mode/apps.rs index c62f1fc..9b3e862 100644 --- a/src/mode/apps.rs +++ b/src/mode/apps.rs @@ -1,4 +1,8 @@ +use std::cmp::Reverse; +use std::collections::HashMap; use std::ffi::CString; +use std::fs::File; +use std::io::{BufRead, BufReader, Write}; use crate::draw::ListItem; use crate::DesktopEntry; @@ -6,11 +10,42 @@ use crate::DesktopEntry; pub struct AppsMode { entries: Vec, term: Vec, + usage: HashMap, } impl AppsMode { - pub fn new(entries: Vec, term: Vec) -> Self { - Self { entries, term } + pub fn new(mut entries: Vec, term: Vec) -> Self { + let usage: HashMap = crate::XDG_DIRS + .place_cache_file(concat!(crate::prog_name!(), ".cache")) + .and_then(File::open) + .map_err(|e| log::error!("cannot open cache file: {}", e)) + .map(BufReader::new) + .into_iter() + .flat_map(|rdr| { + rdr.lines() + .filter(|l| l.as_ref().map(|l| !l.is_empty()).unwrap_or(true)) + .map(|l| { + let line = l.map_err(|e| log::error!("unable to read the line: {}", e))?; + let mut iter = line.split(' '); + let (count, progname) = (iter.next().ok_or(())?, iter.next().ok_or(())?); + + let count = count.parse().map_err(|e| { + log::error!("unable to parse count (\"{}\"): {}", count, e) + })?; + + Ok((progname.to_string(), count)) + }) + }) + .collect::>() + .unwrap_or_default(); + + entries.sort_by_key(|e| Reverse(usage.get(&e.desktop_fname).unwrap_or(&0))); + + Self { + entries, + term, + usage, + } } pub fn eval(&mut self, idx: usize) -> std::convert::Infallible { @@ -30,6 +65,28 @@ impl AppsMode { } else { (&args[0], &args[1..]) }; + + *self.usage.entry(entry.desktop_fname.clone()).or_default() += 1; + + if let Err(e) = crate::XDG_DIRS + .place_cache_file(concat!(crate::prog_name!(), ".cache")) + .and_then(File::create) + .and_then(|mut f| { + let mut buf = vec![]; + + for (progname, count) in &self.usage { + let s = format!("{} ", count); + buf.extend(s.as_bytes()); + buf.extend(progname.as_bytes()); + buf.push(b'\n'); + } + + f.write_all(&buf) + }) + { + log::error!("failed to update cache: {}", e); + } + log::debug!("executing command: {:?} {:?}", prog, args); nix::unistd::execvp(prog, args).unwrap();