summaryrefslogtreecommitdiff
path: root/src/sheet
diff options
context:
space:
mode:
Diffstat (limited to 'src/sheet')
-rw-r--r--src/sheet/cell.rs198
-rw-r--r--src/sheet/mod.rs98
-rw-r--r--src/sheet/register.rs60
3 files changed, 356 insertions, 0 deletions
diff --git a/src/sheet/cell.rs b/src/sheet/cell.rs
new file mode 100644
index 0000000..413e299
--- /dev/null
+++ b/src/sheet/cell.rs
@@ -0,0 +1,198 @@
+use std::fmt::Display;
+
+use mlua::{FromLua, IntoLua, UserData, Value};
+
+use super::register::{Register, SheetId};
+
+#[derive(Debug, Clone)]
+pub enum Cell {
+ String(String),
+ Number(f64),
+}
+
+impl Cell {
+ pub fn new_empty() -> Self {
+ Cell::String("".to_string())
+ }
+}
+
+impl<'lua> IntoLua<'lua> for Cell {
+ fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
+ match self {
+ Cell::String(s) => s.into_lua(lua),
+ Cell::Number(n) => n.into_lua(lua),
+ }
+ }
+}
+
+impl<'lua> FromLua<'lua> for Cell {
+ fn from_lua(value: mlua::Value<'lua>, _: &'lua mlua::Lua) -> mlua::Result<Self> {
+ match value {
+ Value::Nil => Ok(Cell::new_empty()),
+ Value::Boolean(b) => Ok(Cell::String(b.to_string())),
+ Value::Integer(n) => Ok(Cell::String(n.to_string())),
+ Value::Number(n) => Ok(Cell::String(n.to_string())),
+ Value::String(s) => Ok(Cell::String(s.to_str()?.to_string())),
+ _ => Err(mlua::Error::runtime(
+ "cell content must be number or string",
+ )),
+ }
+ }
+}
+
+impl Display for Cell {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Cell::String(s) => s.fmt(f),
+ Cell::Number(n) => n.fmt(f),
+ }
+ }
+}
+
+impl From<String> for Cell {
+ fn from(value: String) -> Self {
+ Cell::String(value.clone())
+ }
+}
+
+impl From<&str> for Cell {
+ fn from(value: &str) -> Self {
+ Cell::String(value.to_string())
+ }
+}
+
+macro_rules! cell_from_integer {
+ ($($number:ty),+ $(,)?) => {
+ $(
+ impl From<$number> for Cell {
+ fn from(value: $number) -> Self {
+ Cell::Number(value as f64)
+ }
+ }
+ )*
+ };
+}
+
+#[rustfmt::skip]
+cell_from_integer!(
+ i8, i16, i32, i64, i128, isize,
+ u8, u16, u32, u64, u128, usize,
+ f32, f64
+);
+
+#[derive(Debug, Clone, Copy)]
+pub struct CellRef {
+ row: usize,
+ column: usize,
+ sheet_id: SheetId,
+}
+
+impl CellRef {
+ pub fn 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_id,
+ row,
+ column,
+ })
+ })
+ .unwrap_or(None)
+ }
+
+ pub fn value(&self) -> Cell {
+ return Register::get(self.sheet_id)
+ .unwrap()
+ .read()
+ .unwrap()
+ .get_cell(self.row, self.column)
+ .unwrap()
+ .clone();
+ }
+
+ pub fn set_value(&mut self, value: Cell) {
+ Register::get(self.sheet_id)
+ .unwrap()
+ .write()
+ .unwrap()
+ .set_cell(self.row, self.column, value);
+ }
+
+ pub fn left(&self) -> Option<Self> {
+ if self.column > 0 {
+ Self::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)
+ }
+
+ pub fn up(&self) -> Option<Self> {
+ if self.row > 0 {
+ Self::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)
+ }
+
+ pub fn begin(&self) -> Option<Self> {
+ Self::new(self.sheet_id, self.row, 0)
+ }
+
+ pub fn end(&self) -> Option<Self> {
+ Self::new(
+ self.sheet_id,
+ self.row,
+ Register::get(self.sheet_id)
+ .unwrap()
+ .read()
+ .unwrap()
+ .width()
+ - 1,
+ )
+ }
+
+ pub fn top(&self) -> Option<Self> {
+ Self::new(self.sheet_id, 0, self.column)
+ }
+
+ pub fn bottom(&self) -> Option<Self> {
+ Self::new(
+ self.sheet_id,
+ Register::get(self.sheet_id)
+ .unwrap()
+ .read()
+ .unwrap()
+ .height()
+ - 1,
+ self.column,
+ )
+ }
+}
+
+impl UserData for CellRef
+where
+ Self: Sized,
+{
+ fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_method_get("row", |_, this| Ok(this.row));
+ fields.add_field_method_get("column", |_, this| Ok(this.column));
+ fields.add_field_method_get("value", |_, this| Ok(this.value()));
+ fields.add_field_method_set("value", |_, this, value| Ok(this.set_value(value)));
+ fields.add_field_method_get("left", |lua, this| this.left().into_lua(lua));
+ fields.add_field_method_get("right", |lua, this| this.right().into_lua(lua));
+ fields.add_field_method_get("up", |lua, this| this.up().into_lua(lua));
+ fields.add_field_method_get("down", |lua, this| this.down().into_lua(lua));
+ fields.add_field_method_get("begin", |lua, this| this.begin().into_lua(lua));
+ fields.add_field_method_get("end", |lua, this| this.end().into_lua(lua));
+ fields.add_field_method_get("top", |lua, this| this.top().into_lua(lua));
+ fields.add_field_method_get("bottom", |lua, this| this.bottom().into_lua(lua));
+ }
+}
diff --git a/src/sheet/mod.rs b/src/sheet/mod.rs
new file mode 100644
index 0000000..015e3d8
--- /dev/null
+++ b/src/sheet/mod.rs
@@ -0,0 +1,98 @@
+use cell::{Cell, CellRef};
+use mlua::{IntoLua, UserData, Value};
+use register::{Register, SheetId};
+
+pub mod cell;
+pub mod register;
+
+#[derive(Debug, Default, Clone)]
+pub struct Sheet {
+ id: register::SheetId,
+ rows: Vec<Vec<Cell>>,
+}
+
+impl Sheet {
+ pub(self) fn new(width: usize, height: usize, id: register::SheetId) -> Self {
+ Self {
+ id,
+ rows: vec![vec![Cell::new_empty(); width]; height],
+ }
+ }
+
+ pub fn set_cell(&mut self, row: usize, column: usize, cell: Cell) {
+ if row < self.height() && column < self.width() {
+ self.rows[row][column] = cell
+ }
+ }
+
+ pub fn get_cell<'a>(&'a self, row: usize, column: usize) -> Option<&'a Cell> {
+ if let Some(r) = self.rows.get(row) {
+ if let Some(_) = r.get(column) {
+ return Some(&self.rows[row][column]);
+ }
+ }
+
+ None
+ }
+
+ pub fn get_ref(&self, row: usize, column: usize) -> Option<CellRef> {
+ CellRef::new(self.id, row, column)
+ }
+
+ pub fn height(&self) -> usize {
+ self.rows.len()
+ }
+
+ pub fn set_height(&mut self, mut height: usize) {
+ height = height.max(1);
+ self.rows
+ .resize(height, vec![Cell::new_empty(); self.width()])
+ }
+
+ pub fn width(&self) -> usize {
+ self.rows.get(0).map(|r| r.len()).unwrap_or(1)
+ }
+
+ pub fn set_width(&mut self, mut width: usize) {
+ width = width.max(1);
+ for row in self.rows.iter_mut() {
+ row.resize(width, Cell::new_empty());
+ }
+ }
+
+ pub fn id(&self) -> register::SheetId {
+ self.id
+ }
+}
+
+struct SheetLuaRef {
+ id: SheetId,
+}
+
+impl SheetLuaRef {
+ pub fn new(id: SheetId) -> Self {
+ Self { id }
+ }
+}
+
+impl UserData for SheetLuaRef {
+ fn add_fields<'lua, F: mlua::UserDataFields<'lua, Self>>(fields: &mut F) {
+ fields.add_field_method_get("width", |_, luaref| {
+ Ok(Register::get(luaref.id).unwrap().read().unwrap().width())
+ });
+
+ fields.add_field_method_get("height", |_, luaref| {
+ Ok(Register::get(luaref.id).unwrap().read().unwrap().height())
+ });
+ }
+
+ 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())
+ } else {
+ Ok(Value::Nil)
+ }
+ })
+ }
+}
diff --git a/src/sheet/register.rs b/src/sheet/register.rs
new file mode 100644
index 0000000..aa5245b
--- /dev/null
+++ b/src/sheet/register.rs
@@ -0,0 +1,60 @@
+use std::sync::{Arc, RwLock};
+
+use mlua::{UserData, Value};
+
+use super::{Sheet, SheetLuaRef};
+
+static REGISTER: RwLock<Vec<Arc<RwLock<Sheet>>>> = RwLock::new(Vec::new());
+
+pub type SheetId = usize;
+
+#[derive(Debug)]
+pub struct Register;
+
+impl Register {
+ pub fn create(width: usize, height: usize) -> SheetId {
+ let mut register = REGISTER.write().unwrap();
+
+ let id = register.len();
+ let sheet = Sheet::new(width, height, id);
+ register.push(Arc::new(RwLock::new(sheet)));
+ id
+ }
+
+ pub fn get<'a>(id: SheetId) -> Option<Arc<RwLock<Sheet>>> {
+ let register = REGISTER.read().unwrap();
+
+ if id < register.len() {
+ Some(Arc::clone(&register[id]))
+ } else {
+ None
+ }
+ }
+}
+
+impl UserData for Register {
+ fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_function_mut("create", |lua, (width, height): (usize, usize)| {
+ let id = Register::create(width, height);
+ let luaref = SheetLuaRef::new(id);
+ if let Ok(ud) = lua.create_userdata(luaref) {
+ Ok(Value::UserData(ud))
+ } else {
+ Ok(Value::Nil)
+ }
+ });
+
+ methods.add_function_mut("get", |lua, id: SheetId| {
+ if let Some(_) = Register::get(id) {
+ let luaref = SheetLuaRef::new(id);
+ if let Ok(ud) = lua.create_userdata(luaref) {
+ Ok(Value::UserData(ud))
+ } else {
+ Ok(Value::Nil)
+ }
+ } else {
+ Ok(Value::Nil)
+ }
+ })
+ }
+}