pub mod application; pub mod calculator; use std::sync::Arc; use itertools::Itertools; use mlua::{ prelude::*, LuaSerdeExt, }; 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; 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: String) -> Result; 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 LuaUserData for _Lense { fn add_fields>(fields: &mut F) { fields.add_field_method_get("stale", |_, this| { Ok(matches!(this.0.get_cache(), Cache::Stale)) }); fields.add_field("name", T::NAME); } fn add_methods>(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) => this.0.filter(entries.clone(), input), Cache::Stale => { 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("interrupt", |_, this, _: ()| Ok(this.0.set_interrupt(true))); } }