aboutsummaryrefslogtreecommitdiff
path: root/.config/awesome/quarrel/native/src/lenses
diff options
context:
space:
mode:
Diffstat (limited to '.config/awesome/quarrel/native/src/lenses')
-rw-r--r--.config/awesome/quarrel/native/src/lenses/application.rs108
-rw-r--r--.config/awesome/quarrel/native/src/lenses/calculator.rs89
-rw-r--r--.config/awesome/quarrel/native/src/lenses/mod.rs121
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)));
}
}