use std::{ sync::{mpsc::channel, Arc, RwLock, RwLockReadGuard}, thread::JoinHandle, }; use mlua::prelude::*; use super::{cell::Cell, Sheet}; pub trait EvalFunction { fn eval_function( &self, func: String, range: Vec<(usize, usize)>, ) -> JoinHandle>; } impl EvalFunction for Arc> { fn eval_function( &self, func_text: String, range: Vec<(usize, usize)>, ) -> JoinHandle> { *self.read().unwrap().progress.lock().unwrap() = 0; let this = Arc::clone(self); std::thread::spawn(move || { let chunks = range.chunks( (range.len() / std::thread::available_parallelism().unwrap()) .min(range.len()) .max(1000), ); if chunks.len() > 4 { let (tx, rx) = channel(); std::thread::scope(|s| { for chunk in chunks { let tx = tx.clone(); let this = Arc::clone(&this); let func_text = func_text.clone(); s.spawn(move || { let tx = tx.clone(); let read_sheet = this.read().unwrap(); let result = EvalRange::new(chunk.iter(), func_text, read_sheet); match result { Ok(evaluator) => { for result in evaluator { if result.is_err() { tx.send(result).unwrap(); break; } tx.send(result).unwrap(); } } Err(error) => tx.send(Err(error)).unwrap(), } }); } drop(tx); let read_sheet = this.read().unwrap(); let mut sheet = read_sheet.clone(); let mut count = 0; for result in rx { count += 1; *read_sheet.progress.lock().unwrap() = (count * 100 / range.len()) as u8; match result { Ok((row, column, cell)) => sheet.set_cell(row, column, cell), Err(error) => return Err(error.to_string()), } } crate::lua::new_instance().unwrap().expire_registry_values(); Ok(sheet) }) } else { let read_sheet = this.read().unwrap(); let mut sheet = read_sheet.clone(); let result = EvalRange::new((&range).iter(), func_text, read_sheet); match result { Ok(evaluator) => { for result in evaluator { match result { Ok((row, column, cell)) => sheet.set_cell(row, column, cell), Err(error) => return Err(error.to_string()), } } } Err(error) => return Err(error), } crate::lua::new_instance().unwrap().expire_registry_values(); Ok(sheet) } }) } } struct EvalRange<'a, I> where I: Iterator, { lua: Lua, reg_func_key: LuaRegistryKey, range: I, read_sheet: RwLockReadGuard<'a, Sheet>, } impl<'a, I> EvalRange<'a, I> where I: Iterator, { fn new( range: I, script: String, read_sheet: RwLockReadGuard<'a, Sheet>, ) -> Result { let lua = crate::lua::new_instance().unwrap(); let result = lua .load(script.clone()) .set_name("Temp Script") .eval::(); if result.is_err() { return Err(result.err().unwrap().to_string()); } let lua = crate::lua::new_instance().unwrap(); let func = lua .load(script) .set_name("Temp Script") .eval::() .unwrap(); Ok(Self { reg_func_key: lua.create_registry_value(func).unwrap(), lua, range, read_sheet, }) } } impl<'a, I> Iterator for EvalRange<'a, I> where I: Iterator, { type Item = Result<(usize, usize, Cell), String>; fn next(&mut self) -> Option { let func: LuaFunction = self.lua.registry_value(&self.reg_func_key).unwrap(); if let Some((row, column)) = self.range.next() { let cellref = self.read_sheet.get_ref(*row, *column); match func.call::<_, Cell>(cellref) { Ok(cell) => Some(Ok((*row, *column, cell))), Err(error) => Some(Err(error.to_string())), } } else { None } } }