diff options
Diffstat (limited to 'src/sheet/eval.rs')
| -rw-r--r-- | src/sheet/eval.rs | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/src/sheet/eval.rs b/src/sheet/eval.rs new file mode 100644 index 0000000..4633949 --- /dev/null +++ b/src/sheet/eval.rs @@ -0,0 +1,170 @@ +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<Result<Sheet, String>>; +} + +impl EvalFunction for Arc<RwLock<Sheet>> { + fn eval_function( + &self, + func_text: String, + range: Vec<(usize, usize)>, + ) -> JoinHandle<Result<Sheet, String>> { + *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<Item = &'a (usize, usize)>, +{ + lua: Lua, + reg_func_key: LuaRegistryKey, + range: I, + read_sheet: RwLockReadGuard<'a, Sheet>, +} + +impl<'a, I> EvalRange<'a, I> +where + I: Iterator<Item = &'a (usize, usize)>, +{ + fn new( + range: I, + script: String, + read_sheet: RwLockReadGuard<'a, Sheet>, + ) -> Result<Self, String> { + let lua = crate::lua::new_instance().unwrap(); + let result = lua + .load(script.clone()) + .set_name("Temp Script") + .eval::<LuaFunction>(); + + 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::<LuaFunction>() + .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<Item = &'a (usize, usize)>, +{ + type Item = Result<(usize, usize, Cell), String>; + + fn next(&mut self) -> Option<Self::Item> { + 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 + } + } +} |