use std::sync::{ Arc, LazyLock, Mutex, atomic::{ AtomicBool, Ordering, }, }; use fend_core::{ Context, Interrupt, evaluate_with_interrupt, }; use mlua::prelude::*; use crate::lenses::{ Cache, Entries, Entry, Lense, }; static CTX: LazyLock> = 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"; const PREFIX: Option<&'static str> = Some("#"); fn init() -> std::sync::Arc { 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); } #[inline] fn get_interrupt(&self) -> bool { self.should_interrupt.load(Ordering::Relaxed) } fn entries(&self, _: &Lua, input: &str) -> Result { let result = match evaluate_with_interrupt( input.trim(), &mut CTX.lock().expect("Failed to acquire Fend context lock"), self, ) { Ok(result) => result.get_main_result().to_string(), Err(err) => err, }; Ok(if result.is_empty() { Entries::None } else { Entries::Single(Entry { message: result, exec: None, }) }) } #[inline] fn filter(&self, entries: Entries, _: &str) -> Entries { entries } } impl Interrupt for Calculator { fn should_interrupt(&self) -> bool { self.should_interrupt.load(Ordering::Relaxed) } }