use std::{ cell::RefCell, fs::File, fs::OpenOptions, io::{ Read, Seek, Write }, rc::Rc, str::FromStr, }; use html_escape::decode_html_entities_to_string; use mlua::prelude::*; use serde::Serialize; pub fn decode_html(_: &Lua, string: String) -> LuaResult { let mut output = String::new(); decode_html_entities_to_string(string, &mut output); Ok(output) } enum ReadMode { Line, Number, All, } impl FromStr for ReadMode { type Err = (); fn from_str(value: &str) -> Result { Ok(match value { "l" => Self::Line, "n" => Self::Number, _ => Self::All, }) } } #[derive(Serialize)] #[serde(untagged)] enum StringOrNumber { String(String), Number(u64), } pub struct FileHandle(Rc>); impl LuaUserData for FileHandle { fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) { methods.add_method("read", |lua, this: &FileHandle, mode: String| { let content = this.read()?; Ok(lua.to_value(&match ReadMode::from_str(&mode).unwrap() { ReadMode::Line => StringOrNumber::String( content .lines() .next() .map_or_else(String::new, |slice| slice.trim().to_owned()), ), ReadMode::Number => StringOrNumber::Number( content.trim().parse::().map_err(LuaError::external)?, ), ReadMode::All => StringOrNumber::String(content), })) }); methods.add_method("write", |_, this: &FileHandle, content: String| { this.write(content.as_bytes()) }); methods.add_method("lines", |_, this: &FileHandle, (): ()| { Ok(this .read()? .lines() .map(ToOwned::to_owned) .collect::>()) }); methods.add_method("rewind", |_, this: &FileHandle, (): ()| this.rewind()); } } impl FileHandle { pub fn new(_: &Lua, path: String) -> LuaResult { Ok(Self(Rc::new(RefCell::new(File::open(path)?)))) // Ok(Self(Rc::new(RefCell::new(OpenOptions::new().write(true).read(true).open(path)?)))) } fn read(&self) -> LuaResult { let mut content = String::new(); self.0.borrow_mut().read_to_string(&mut content)?; Ok(content) } fn rewind(&self) -> LuaResult<()> { self.0.borrow_mut().rewind().map_err(LuaError::external) } fn write(&self, buf: &[u8]) -> LuaResult { self.0.borrow_mut().write(buf).map_err(LuaError::external) } }