pub mod application; pub mod calculator; use std::sync::Arc; use itertools::Itertools; use mlua::{ LuaSerdeExt, prelude::*, }; use rayon::iter::FromParallelIterator; use serde::{ Deserialize, Serialize, }; #[derive(Clone, Debug)] pub enum Entries { Multiple(Vec), Single(Entry), None, } impl FromIterator for Entries { fn from_iter>(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> for Entries { fn from(entries: Vec) -> 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 { 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)>, } impl IntoLua for Entry { fn into_lua(self, lua: &Lua) -> LuaResult { return lua.to_value(&self); } } impl FromLua for Entry { fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult { return lua.from_value(value); } } #[derive(Default, Clone, Debug)] pub enum Cache { Valid(Entries), #[default] Stale, } pub struct _Lense(pub Arc); pub trait Lense { const NAME: &'static str; const PREFIX: Option<&'static str>; fn init() -> Arc; fn set_cache(&self, cache: Cache); fn get_cache(&self) -> Cache; fn set_interrupt(&self, interrupt: bool); fn get_interrupt(&self) -> bool; fn entries(&self, lua: &Lua, input: &str) -> Result; fn filter(&self, entries: Entries, input: &str) -> 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 LuaUserData for _Lense { fn add_fields>(fields: &mut F) { fields.add_field_method_get("stale", |_, Self(this)| { Ok(matches!(this.get_cache(), Cache::Stale)) }); fields.add_field("name", T::NAME); } fn add_methods>(methods: &mut M) { methods.add_method_mut("query", |lua, Self(this), input: String| { if T::PREFIX.is_some_and(|prefix| input.starts_with(prefix)) || T::PREFIX.is_none() { let input = if let Some(prefix) = T::PREFIX { input.trim_start_matches(prefix) } else { &input } .trim(); this.set_interrupt(false); // reset interrupt so that we can use the lense again return Ok(match this.get_cache() { Cache::Valid(entries) => this.filter(entries.clone(), input), Cache::Stale => { let entries = this.entries(lua, input).map_err(LuaError::external)?; let mut entries = this.filter(entries, input); match entries { Entries::Multiple(ref mut entries) => { entries.sort_by(|a, b| a.message.cmp(&b.message)) } _ => {} } this.set_cache(Cache::Valid(entries.clone())); entries } }); } else { return Ok(Entries::None); } }); methods.add_method_mut("mark_stale", |_, Self(this), _: ()| { Ok(this.set_cache(Cache::Stale)) }); methods.add_method_mut("interrupt", |_, Self(this), _: ()| { Ok(this.set_interrupt(true)) }); } }