summaryrefslogtreecommitdiff
path: root/src/sheet/eval.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/sheet/eval.rs')
-rw-r--r--src/sheet/eval.rs170
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
+ }
+ }
+}