diff options
| author | delta <darkussdelta@gmail.com> | 2025-10-29 16:35:38 +0100 |
|---|---|---|
| committer | delta <darkussdelta@gmail.com> | 2025-10-29 16:35:38 +0100 |
| commit | d7c66522cf365f516babcfeb1d4a2d36c3ea41af (patch) | |
| tree | 30c7d6103037b31170ae6d8fd58e3849e3cea823 /.config/awesome/quarrel/native/src/lenses | |
| parent | df418700b7d776f03ee5b58daa2d497cddb45aca (diff) | |
a small refactor
Diffstat (limited to '.config/awesome/quarrel/native/src/lenses')
| -rw-r--r-- | .config/awesome/quarrel/native/src/lenses/application.rs | 108 | ||||
| -rw-r--r-- | .config/awesome/quarrel/native/src/lenses/calculator.rs | 89 | ||||
| -rw-r--r-- | .config/awesome/quarrel/native/src/lenses/mod.rs | 121 |
3 files changed, 250 insertions, 68 deletions
diff --git a/.config/awesome/quarrel/native/src/lenses/application.rs b/.config/awesome/quarrel/native/src/lenses/application.rs index 89b7bb4..38a7762 100644 --- a/.config/awesome/quarrel/native/src/lenses/application.rs +++ b/.config/awesome/quarrel/native/src/lenses/application.rs @@ -1,58 +1,118 @@ use std::{ - fs::read_dir, path::PathBuf + any::type_name, + fs::read_dir, + path::PathBuf, + sync::{ + atomic::{ + AtomicBool, + Ordering, + }, + Arc, + OnceLock, + RwLock, + }, }; use freedesktop_entry_parser as fd; use mlua::prelude::*; +use notify::RecommendedWatcher; use rayon::prelude::*; use url::Url; use crate::lenses::{ - Entry, - Lense, - Cache + Cache, Entries, Entry, Lense }; +static APPS_DIR: &'static str = "/usr/share/applications"; +static WATCHER: OnceLock<RecommendedWatcher> = OnceLock::new(); + #[derive(Default)] -pub struct Application(pub Cache); +pub struct Application { + cache: RwLock<Cache>, + should_interrupt: AtomicBool, +} impl Lense for Application { const NAME: &str = "Application"; - fn get_cache(&self) -> &Cache { - &self.0 + fn init() -> Arc<Self> { + let this = Arc::new(Application::default()); + let watcher_this = this.clone(); + WATCHER + .set( + notify::recommended_watcher(move |event| { + match event { + Ok(_) => { + // We don't care what specifically changed, just that *it did* + watcher_this.set_cache(Cache::Stale); + } + Err(err) => { + eprintln!("Watch error: {:?}", err) + } + } + }) + .expect("Failed to instantiate a watcher"), + ) + .expect("Failed to set a watcher"); + this + } + + #[inline] + fn set_cache(&self, cache: Cache) { + if let Err(err) = self.cache.write().map(|mut place| *place = cache) { + eprintln!( + "Failed to write cache value for {}: {:?}", + type_name::<Self>(), + err + ) + } + } + + #[inline] + fn get_cache(&self) -> Cache { + match self.cache.read() { + Ok(ok) => ok.clone(), + Err(err) => { + eprintln!( + "Failed to read cache value for {}: {:?}", + type_name::<Self>(), + err + ); + Cache::Stale + } + } + } + + #[inline] + fn set_interrupt(&self, interrupt: bool) { + // self.should_interrupt.store(interrupt, Ordering::Relaxed) } - fn set_cache(&mut self, cache: Cache) { - self.0 = cache; + #[inline] + fn get_interrupt(&self) -> bool { + false + // self.should_interrupt.load(Ordering::Relaxed) } - fn query(_: &Lua, input: String) -> Result<Vec<Entry>, anyhow::Error> { - let applications_dir = "/usr/share/applications"; - let entries = read_dir(applications_dir)? + fn entries(&self, _: &Lua, _: String) -> Result<Entries, anyhow::Error> { + let entries = read_dir(APPS_DIR)? .map(|result| result.map(|e| e.path())) .collect::<Result<Vec<_>, std::io::Error>>()?; - let parsed_entries: Vec<Entry> = entries + let parsed_entries: Entries = entries .into_par_iter() .filter(|path| path.extension().is_some_and(|ext| ext == "desktop")) - .filter_map(|path| { - parse_entry(path).ok().flatten() - }) - .collect(); + .filter_map(|path| parse_entry(path).ok().flatten()) + .collect::<Vec<Entry>>() + .into(); - Ok( - parsed_entries - .into_iter() - .filter(|entry| entry.message.to_lowercase().contains(&input.to_lowercase())) - .collect(), - ) + Ok(parsed_entries) } } fn parse_entry(path: PathBuf) -> Result<Option<Entry>, ()> { let Ok(entry) = fd::parse_entry(&path) else { - return Err(()) + return Err(()); }; let section = entry.section("Desktop Entry"); diff --git a/.config/awesome/quarrel/native/src/lenses/calculator.rs b/.config/awesome/quarrel/native/src/lenses/calculator.rs index 640bdeb..36f9805 100644 --- a/.config/awesome/quarrel/native/src/lenses/calculator.rs +++ b/.config/awesome/quarrel/native/src/lenses/calculator.rs @@ -1,40 +1,93 @@ -// use meval::eval_str; -use cpc::{ - eval, - units::Unit, +use std::sync::{ + atomic::{ + AtomicBool, + Ordering, + }, + Arc, + LazyLock, + Mutex, +}; + +use fend_core::{ + evaluate_with_interrupt, + Context, + Interrupt, }; use mlua::prelude::*; use crate::lenses::{ - Entry, - Cache, - Lense + Cache, Entries, Entry, Lense }; -pub struct Calculator; +static CTX: LazyLock<Mutex<Context>> = LazyLock::new(|| { + let mut ctx = Context::new(); + ctx.use_coulomb_and_farad(); + Mutex::new(ctx) +}); + +#[derive(Default)] +pub struct Calculator { + should_interrupt: AtomicBool, +} impl Lense for Calculator { const NAME: &str = "Calculator"; - fn get_cache(&self) -> &Cache { - &Cache::Stale + fn init() -> std::sync::Arc<Self> { + Arc::new(Calculator::default()) + } + + #[inline] + fn set_cache(&self, _: Cache) {} + + #[inline] + fn get_cache(&self) -> Cache { + Cache::Stale + } + + #[inline] + fn set_interrupt(&self, interrupt: bool) { + self.should_interrupt.store(interrupt, Ordering::Relaxed); } - fn set_cache(&mut self, _: Cache) {} + #[inline] + fn get_interrupt(&self) -> bool { + self.should_interrupt.load(Ordering::Relaxed) + } - fn query(_: &Lua, input: String) -> Result<Vec<Entry>, anyhow::Error> { - let result = match eval(input.trim(), true, Unit::Celsius, false) { + fn entries(&self, _: &Lua, input: String) -> Result<Entries, anyhow::Error> { + let result = match evaluate_with_interrupt( + input.trim(), + &mut CTX.lock().expect("Failed to acquire Fend context lock"), + self, + ) { Ok(result) => { - format!("{result}") + result.get_main_result().to_string() + } + Err(err) => { + err } - Err(err) => { return Err(anyhow::anyhow!(err)); }, }; - Ok(vec![Entry { + Ok(if result.is_empty() { + Entries::None + } else { + Entries::Single(Entry { message: result, exec: None, - }; 1] - ) + }) + }) + + } + + #[inline] + fn filter(&self, entries: Entries, _: String) -> Entries { + entries } } +impl Interrupt for Calculator { + fn should_interrupt(&self) -> bool { + self.should_interrupt.load(Ordering::Relaxed) + } +} diff --git a/.config/awesome/quarrel/native/src/lenses/mod.rs b/.config/awesome/quarrel/native/src/lenses/mod.rs index 6eb5b58..bb7f727 100644 --- a/.config/awesome/quarrel/native/src/lenses/mod.rs +++ b/.config/awesome/quarrel/native/src/lenses/mod.rs @@ -1,13 +1,66 @@ pub mod application; pub mod calculator; +use std::sync::Arc; + +use itertools::Itertools; use mlua::{ prelude::*, LuaSerdeExt, }; -use serde::{Serialize, Deserialize}; +use rayon::iter::FromParallelIterator; +use serde::{ + Deserialize, + Serialize, +}; + +#[derive(Clone, Debug)] +pub enum Entries { + Multiple(Vec<Entry>), + Single(Entry), + None +} -#[derive(Deserialize, Serialize, Clone)] +impl FromIterator<Entry> for Entries { + fn from_iter<T: IntoIterator<Item = Entry>>(iter: T) -> Self { + let mut iter = iter.into_iter(); + match (iter.next(), iter.next()) { + (None, None) => Self::None, + (Some(first), None) => Self::Single(first), + (None, Some(_)) => unreachable!(), + (Some(first), Some(second)) => { + let mut vec = Vec::from([first, second]); + vec.extend(iter); + Self::Multiple(vec) + }, + } + } +} + +impl From<Vec<Entry>> for Entries { + fn from(entries: Vec<Entry>) -> Self { + match entries.len() { + 0 => Self::None, + 1 => { + let entry = entries.into_iter().exactly_one().unwrap(); + Self::Single(entry) + }, + _ => Self::Multiple(entries) + } + } +} + +impl IntoLua for Entries { + fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> { + match self { + Entries::Multiple(entries) => entries.into_lua(lua), + Entries::Single(entry) => entry.into_lua(lua), + Entries::None => Ok(LuaValue::Nil) + } + } +} + +#[derive(Deserialize, Serialize, Clone, Debug)] pub struct Entry { pub message: String, pub exec: Option<(String, bool)>, @@ -15,7 +68,7 @@ pub struct Entry { impl IntoLua for Entry { fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> { - return lua.to_value(&self) + return lua.to_value(&self); } } @@ -25,55 +78,71 @@ impl FromLua for Entry { } } -#[derive(Default)] +#[derive(Default, Clone, Debug)] pub enum Cache { - Valid(Vec<Entry>), + Valid(Entries), #[default] - Stale + Stale, } -pub struct _Lense<T: Lense>(pub T); +pub struct _Lense<T: Lense>(pub Arc<T>); pub trait Lense { const NAME: &'static str; - fn set_cache(&mut self, cache: Cache); - fn get_cache(&self) -> &Cache; + fn init() -> Arc<Self>; + + fn set_cache(&self, cache: Cache); + fn get_cache(&self) -> Cache; + + fn set_interrupt(&self, interrupt: bool); + fn get_interrupt(&self) -> bool; - fn query(lua: &Lua, input: String) -> Result<Vec<Entry>, anyhow::Error>; + fn entries(&self, lua: &Lua, input: String) -> Result<Entries, anyhow::Error>; + fn filter(&self, entries: Entries, input: String) -> Entries { + let entry_contains = |entry: &Entry| { + entry.message.to_lowercase().contains(&input.to_lowercase()) + }; + match entries { + Entries::Multiple(entries) => entries.into_iter() + .filter(entry_contains) + .collect(), + Entries::Single(entry) => if entry_contains(&entry) { Entries::Single(entry) } else { Entries::None } + Entries::None => Entries::None + } + } } impl<T: Lense + 'static> LuaUserData for _Lense<T> { fn add_fields<F: LuaUserDataFields<Self>>(fields: &mut F) { - // fields.add_field_method_get("cache", |lua, this| { - // let cache = this.0.get_cache(); - // match cache { - // Cache::Valid(cache) => Ok(cache.clone().into_lua(lua)?), - // Cache::Stale => Ok(LuaNil) - // } - // }); - - // fields.add_field_method_set("cache", |_, this, cache: Vec<Entry>| { - // Ok(this.0.set_cache(Cache::Valid(cache))) - // }); - - fields.add_field_method_get("stale", |_, this| Ok(matches!(this.0.get_cache(), Cache::Stale))); + fields.add_field_method_get("stale", |_, this| { + Ok(matches!(this.0.get_cache(), Cache::Stale)) + }); fields.add_field("name", T::NAME); } fn add_methods<M: LuaUserDataMethods<Self>>(methods: &mut M) { methods.add_method_mut("query", |lua, this, input: String| { + this.0.set_interrupt(false); // reset interrupt so that we can use the lense again return Ok(match this.0.get_cache() { - Cache::Valid(entries) => entries.clone(), + Cache::Valid(entries) => this.0.filter(entries.clone(), input), Cache::Stale => { - let entries = T::query(lua, input).map_err(LuaError::external)?; + let entries = this.0.entries(lua, input.clone()).map_err(LuaError::external)?; + let mut entries = this.0.filter(entries, input); + match entries { + Entries::Multiple(ref mut entries) => entries.sort_by(|a, b| a.message.cmp(&b.message)), + _ => {} + } this.0.set_cache(Cache::Valid(entries.clone())); entries } }); }); - methods.add_method_mut("mark_stale", |_, this, _: ()| Ok(this.0.set_cache(Cache::Stale)) ); + methods.add_method_mut("mark_stale", |_, this, _: ()| { + Ok(this.0.set_cache(Cache::Stale)) + }); + methods.add_method_mut("interrupt", |_, this, _: ()| Ok(this.0.set_interrupt(true))); } } |
