use std::fmt::Debug; use automation_cast::Cast; use dyn_clone::DynClone; use google_home::traits::OnOff; use mlua::ObjectLike; use crate::event::{OnDarkness, OnMqtt, OnNotification, OnPresence}; // TODO: Make this a proper macro macro_rules! impl_device { ($device:ty) => { impl mlua::UserData for $device { fn add_methods>(methods: &mut M) { methods.add_async_function("new", |_lua, config| async { let device: $device = LuaDeviceCreate::create(config) .await .map_err(mlua::ExternalError::into_lua_err)?; Ok(device) }); methods.add_method("__box", |_lua, this, _: ()| { let b: Box = Box::new(this.clone()); Ok(b) }); methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) }); if impls::impls!($device: google_home::traits::OnOff) { methods.add_async_method("set_on", |_lua, this, on: bool| async move { (this.deref().cast() as Option<&dyn google_home::traits::OnOff>) .expect("Cast should be valid") .set_on(on) .await .unwrap(); Ok(()) }); methods.add_async_method("is_on", |_lua, this, _: ()| async move { Ok((this.deref().cast() as Option<&dyn google_home::traits::OnOff>) .expect("Cast should be valid") .on() .await .unwrap()) }); } } } }; } pub(crate) use impl_device; #[async_trait::async_trait] pub trait LuaDeviceCreate { type Config; type Error; async fn create(config: Self::Config) -> Result where Self: Sized; } pub trait Device: Debug + DynClone + Sync + Send + Cast + Cast + Cast + Cast + Cast + Cast { fn get_id(&self) -> String; } impl mlua::FromLua for Box { fn from_lua(value: mlua::Value, _lua: &mlua::Lua) -> mlua::Result { match value { mlua::Value::UserData(ud) => { let ud = if ud.is::>() { ud } else { ud.call_method::<_>("__box", ())? }; let b = ud.borrow::()?.clone(); Ok(b) } _ => Err(mlua::Error::RuntimeError("Expected user data".into())), } } } impl mlua::UserData for Box {} dyn_clone::clone_trait_object!(Device);