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> { 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 { 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 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)] pub struct CellRef { row: usize, column: usize, sheet_id: SheetId, cell: Cell, } impl CellRef { 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 { Register::get(sheet_id) .map(|sheet| { 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 { self.cell.clone() } pub fn set_value(&mut self, cell: Cell) { self.cell = cell.clone(); Register::get(self.sheet_id) .unwrap() .write() .unwrap() .set_cell(self.row, self.column, cell); } pub fn left(&self) -> Option { if self.column > 0 { Self::fetch_new(self.sheet_id, self.row, self.column - 1) } else { None } } pub fn right(&self) -> Option { Self::fetch_new(self.sheet_id, self.row, self.column + 1) } pub fn up(&self) -> Option { if self.row > 0 { Self::fetch_new(self.sheet_id, self.row - 1, self.column) } else { None } } pub fn down(&self) -> Option { Self::fetch_new(self.sheet_id, self.row + 1, self.column) } pub fn begin(&self) -> Option { Self::fetch_new(self.sheet_id, self.row, 0) } pub fn end(&self) -> Option { Self::fetch_new( self.sheet_id, self.row, Register::get(self.sheet_id) .unwrap() .read() .unwrap() .width() - 1, ) } pub fn top(&self) -> Option { Self::fetch_new(self.sheet_id, 0, self.column) } pub fn bottom(&self) -> Option { Self::fetch_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_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)); } }