summaryrefslogtreecommitdiff
path: root/src/sheet
diff options
context:
space:
mode:
Diffstat (limited to 'src/sheet')
-rw-r--r--src/sheet/cell.rs46
-rw-r--r--src/sheet/eval.rs170
-rw-r--r--src/sheet/mod.rs60
3 files changed, 208 insertions, 68 deletions
diff --git a/src/sheet/cell.rs b/src/sheet/cell.rs
index 53a4b04..9bee8ca 100644
--- a/src/sheet/cell.rs
+++ b/src/sheet/cell.rs
@@ -80,66 +80,80 @@ cell_from_integer!(
f32, f64
);
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
pub struct CellRef {
row: usize,
column: usize,
sheet_id: SheetId,
+ cell: Cell,
}
impl CellRef {
- pub fn new(sheet_id: SheetId, row: usize, column: usize) -> Option<Self> {
+ pub unsafe fn new(sheet_id: SheetId, row: usize, column: usize, cell: Cell) -> Self {
+ Self {
+ sheet_id,
+ row,
+ column,
+ cell
+ }
+ }
+
+ pub fn fetch_new(sheet_id: SheetId, row: usize, column: usize) -> Option<Self> {
Register::get(sheet_id)
.map(|sheet| {
- sheet.read().unwrap().get_cell(row, column).map(|_| Self {
+ sheet.read().unwrap().get_cell(row, column).map(|cell| Self {
sheet_id,
row,
column,
+ cell: cell.clone(),
})
})
.unwrap_or(None)
}
pub fn value(&self) -> Cell {
- return Register::get(self.sheet_id)
- .unwrap()
- .read()
+ self.cell.clone()
+ }
+
+ pub fn set_value(&mut self, cell: Cell) {
+ self.cell = cell.clone();
+ Register::get(self.sheet_id)
.unwrap()
- .get_cell(self.row, self.column)
+ .write()
.unwrap()
- .clone();
+ .set_cell(self.row, self.column, cell);
}
pub fn left(&self) -> Option<Self> {
if self.column > 0 {
- Self::new(self.sheet_id, self.row, self.column - 1)
+ Self::fetch_new(self.sheet_id, self.row, self.column - 1)
} else {
None
}
}
pub fn right(&self) -> Option<Self> {
- Self::new(self.sheet_id, self.row, self.column + 1)
+ Self::fetch_new(self.sheet_id, self.row, self.column + 1)
}
pub fn up(&self) -> Option<Self> {
if self.row > 0 {
- Self::new(self.sheet_id, self.row - 1, self.column)
+ Self::fetch_new(self.sheet_id, self.row - 1, self.column)
} else {
None
}
}
pub fn down(&self) -> Option<Self> {
- Self::new(self.sheet_id, self.row + 1, self.column)
+ Self::fetch_new(self.sheet_id, self.row + 1, self.column)
}
pub fn begin(&self) -> Option<Self> {
- Self::new(self.sheet_id, self.row, 0)
+ Self::fetch_new(self.sheet_id, self.row, 0)
}
pub fn end(&self) -> Option<Self> {
- Self::new(
+ Self::fetch_new(
self.sheet_id,
self.row,
Register::get(self.sheet_id)
@@ -152,11 +166,11 @@ impl CellRef {
}
pub fn top(&self) -> Option<Self> {
- Self::new(self.sheet_id, 0, self.column)
+ Self::fetch_new(self.sheet_id, 0, self.column)
}
pub fn bottom(&self) -> Option<Self> {
- Self::new(
+ Self::fetch_new(
self.sheet_id,
Register::get(self.sheet_id)
.unwrap()
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
+ }
+ }
+}
diff --git a/src/sheet/mod.rs b/src/sheet/mod.rs
index 18ccee0..11f95c9 100644
--- a/src/sheet/mod.rs
+++ b/src/sheet/mod.rs
@@ -1,13 +1,11 @@
-use std::{
- sync::{Arc, Mutex, RwLock},
- thread::JoinHandle,
-};
+use std::sync::Mutex;
use cell::{Cell, CellRef};
use mlua::prelude::*;
use register::{Register, SheetId};
pub mod cell;
+pub mod eval;
pub mod register;
#[derive(Debug, Default)]
@@ -43,7 +41,9 @@ impl Sheet {
}
pub fn get_ref(&self, row: usize, column: usize) -> Option<CellRef> {
- CellRef::new(self.id, row, column)
+ self.get_cell(row, column).map(|cell| {
+ unsafe { CellRef::new(self.id, row, column, cell.clone()) }
+ })
}
pub fn height(&self) -> usize {
@@ -113,56 +113,12 @@ impl LuaUserData for SheetLuaRef {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method_mut("cell", |lua, luaref, (row, column): (usize, usize)| {
- if let Some(_) = Register::get(luaref.id) {
- Ok(CellRef::new(luaref.id, row, column).into_lua(lua).unwrap())
+ if let Some(rw) = Register::get(luaref.id) {
+ let sheet = rw.read().unwrap();
+ Ok(sheet.get_ref(row, column).into_lua(lua).unwrap())
} else {
Ok(LuaValue::Nil)
}
})
}
}
-
-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>> {
- let this = Arc::clone(self);
- std::thread::spawn(move || {
- let read_sheet = this.read().unwrap();
- *read_sheet.progress.lock().unwrap() = 0;
- let mut sheet = read_sheet.clone();
-
- let lua = crate::lua::new_instance().unwrap();
- let result = lua
- .load(func_text.clone())
- .set_name("Temp Script")
- .eval::<LuaFunction>();
-
- match result {
- Ok(func) => {
- for (i, (row, column)) in range.iter().enumerate() {
- *read_sheet.progress.lock().unwrap() = (i * 100 / range.len()) as u8;
- let cellref = read_sheet.get_ref(*row, *column);
- match func.call::<_, Cell>(cellref) {
- Ok(cell) => sheet.set_cell(*row, *column, cell),
- Err(error) => return Err(error.to_string()),
- }
- }
- }
- Err(error) => return Err(error.to_string()),
- }
-
- Ok(sheet)
- })
- }
-}