diff options
Diffstat (limited to 'src/sheet/cell.rs')
| -rw-r--r-- | src/sheet/cell.rs | 198 |
1 files changed, 198 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)); + } +} |