use std::{borrow::Cow, fmt::Display, io}; use mlua::{ExternalError, Lua, LuaString, MetaMethod, UserData, UserDataFields, UserDataMethods, Value}; use yazi_codegen::FromLuaOwned; use yazi_shim::SStr; #[derive(FromLuaOwned)] pub enum Error { Io(std::io::Error), Fs(yazi_shim::fs::Error), Serde(serde_json::Error), Custom(SStr), } impl From for io::Error { fn from(value: Error) -> Self { match value { Error::Io(e) => e, Error::Fs(e) => e.into(), Error::Serde(e) => Self::other(e), Error::Custom(s) => Self::other(s.into_owned()), } } } impl Error { pub fn install(lua: &Lua) -> mlua::Result<()> { let custom = lua.create_function(|_, msg: String| Ok(Self::custom(msg)))?; let fs = lua.create_function(|_, value: Value| { Ok(Self::Fs(match value { Value::Table(t) => yazi_shim::fs::Error::custom( &t.raw_get::("kind")?.to_str()?, t.raw_get("code")?, &t.raw_get::("message")?.to_str()?, )?, _ => Err("expected a table".into_lua_err())?, })) })?; lua.globals().raw_set("Error", lua.create_table_from([("custom", custom), ("fs", fs)])?) } pub fn custom(msg: impl Into) -> Self { Self::Custom(msg.into()) } pub fn into_string(self) -> SStr { match self { Self::Io(e) => Cow::Owned(e.to_string()), Self::Fs(e) => Cow::Owned(e.to_string()), Self::Serde(e) => Cow::Owned(e.to_string()), Self::Custom(s) => s, } } } impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Io(e) => write!(f, "{e}"), Self::Fs(e) => write!(f, "{e}"), Self::Serde(e) => write!(f, "{e}"), Self::Custom(s) => write!(f, "{s}"), } } } impl UserData for Error { fn add_fields>(fields: &mut F) { fields.add_field_method_get("code", |_, me| { Ok(match me { Self::Io(e) => e.raw_os_error(), Self::Fs(e) => e.raw_os_error(), _ => None, }) }); fields.add_field_method_get("kind", |_, me| { Ok(match me { Self::Io(e) => Some(yazi_shim::fs::Error::from(e.kind()).kind_str()), Self::Fs(e) => Some(e.kind_str()), _ => None, }) }); } fn add_methods>(methods: &mut M) { methods.add_meta_method(MetaMethod::ToString, |lua, me, ()| { Ok(match me { Self::Io(_) | Self::Fs(_) | Self::Serde(_) => lua.create_external_string(me.to_string()), Self::Custom(s) => lua.create_string(&**s), }) }); methods.add_meta_function(MetaMethod::Concat, |lua, (lhs, rhs): (Value, Value)| { match (lhs, rhs) { (Value::String(l), Value::UserData(r)) => { let r = r.borrow::()?; lua.create_external_string([&l.as_bytes(), r.to_string().as_bytes()].concat()) } (Value::UserData(l), Value::String(r)) => { let l = l.borrow::()?; lua.create_external_string([l.to_string().as_bytes(), &r.as_bytes()].concat()) } _ => Err("only string can be concatenated with Error".into_lua_err()), } }); } }