From 5d342afb1f2d4f8ac603a0a06bfc4b23be3f64cf Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Sat, 30 Aug 2025 04:20:27 +0200 Subject: [PATCH] Converted macro to derive macro --- automation_devices/src/air_filter.rs | 6 +- automation_devices/src/contact_sensor.rs | 6 +- automation_devices/src/debug_bridge.rs | 5 +- automation_devices/src/hue_bridge.rs | 5 +- automation_devices/src/hue_group.rs | 6 +- automation_devices/src/hue_switch.rs | 5 +- automation_devices/src/ikea_remote.rs | 5 +- automation_devices/src/kasa_outlet.rs | 6 +- automation_devices/src/light_sensor.rs | 5 +- automation_devices/src/wake_on_lan.rs | 5 +- automation_devices/src/washer.rs | 5 +- automation_devices/src/zigbee/light.rs | 10 +- automation_devices/src/zigbee/outlet.rs | 8 +- automation_lib/src/ntfy.rs | 5 +- automation_lib/src/presence.rs | 5 +- automation_macro/src/impl_device.rs | 112 +++++++++++++---------- automation_macro/src/lib.rs | 6 +- 17 files changed, 108 insertions(+), 97 deletions(-) diff --git a/automation_devices/src/air_filter.rs b/automation_devices/src/air_filter.rs index 971fd74..d3593ae 100644 --- a/automation_devices/src/air_filter.rs +++ b/automation_devices/src/air_filter.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use automation_lib::config::InfoConfig; use automation_lib::device::{Device, LuaDeviceCreate}; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::device::Name; use google_home::errors::ErrorCode; use google_home::traits::{ @@ -19,7 +19,8 @@ pub struct Config { pub url: String, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] +#[traits(OnOff)] pub struct AirFilter { config: Config, } @@ -62,7 +63,6 @@ impl AirFilter { Ok(reqwest::get(url).await?.json().await?) } } -impl_device!(AirFilter); #[async_trait] impl LuaDeviceCreate for AirFilter { diff --git a/automation_devices/src/contact_sensor.rs b/automation_devices/src/contact_sensor.rs index 657e9ff..ebb8768 100644 --- a/automation_devices/src/contact_sensor.rs +++ b/automation_devices/src/contact_sensor.rs @@ -10,7 +10,7 @@ use automation_lib::event::{OnMqtt, OnPresence}; use automation_lib::messages::{ContactMessage, PresenceMessage}; use automation_lib::mqtt::WrappedAsyncClient; use automation_lib::presence::DEFAULT_PRESENCE; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::device; use google_home::errors::{DeviceError, ErrorCode}; use google_home::traits::OpenClose; @@ -61,12 +61,12 @@ struct State { handle: Option>, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] +#[traits(OpenClose)] pub struct ContactSensor { config: Config, state: Arc>, } -impl_device!(ContactSensor); impl ContactSensor { async fn state(&self) -> RwLockReadGuard<'_, State> { diff --git a/automation_devices/src/debug_bridge.rs b/automation_devices/src/debug_bridge.rs index 7e41e2d..700771d 100644 --- a/automation_devices/src/debug_bridge.rs +++ b/automation_devices/src/debug_bridge.rs @@ -6,7 +6,7 @@ use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::{OnDarkness, OnPresence}; use automation_lib::messages::{DarknessMessage, PresenceMessage}; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use tracing::{trace, warn}; #[derive(Debug, LuaDeviceConfig, Clone)] @@ -18,11 +18,10 @@ pub struct Config { pub client: WrappedAsyncClient, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct DebugBridge { config: Config, } -impl_device!(DebugBridge); #[async_trait] impl LuaDeviceCreate for DebugBridge { diff --git a/automation_devices/src/hue_bridge.rs b/automation_devices/src/hue_bridge.rs index b214ca6..0f37c6b 100644 --- a/automation_devices/src/hue_bridge.rs +++ b/automation_devices/src/hue_bridge.rs @@ -4,7 +4,7 @@ use std::net::SocketAddr; use async_trait::async_trait; use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::{OnDarkness, OnPresence}; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use serde::{Deserialize, Serialize}; use tracing::{error, trace, warn}; @@ -29,11 +29,10 @@ pub struct Config { pub flags: FlagIDs, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct HueBridge { config: Config, } -impl_device!(HueBridge); #[derive(Debug, Serialize)] struct FlagMessage { diff --git a/automation_devices/src/hue_group.rs b/automation_devices/src/hue_group.rs index 78fc90e..836776b 100644 --- a/automation_devices/src/hue_group.rs +++ b/automation_devices/src/hue_group.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use anyhow::Result; use async_trait::async_trait; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::errors::ErrorCode; use google_home::traits::OnOff; use tracing::{error, trace, warn}; @@ -19,11 +19,11 @@ pub struct Config { pub scene_id: String, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] +#[traits(OnOff)] pub struct HueGroup { config: Config, } -impl_device!(HueGroup); // Couple of helper function to get the correct urls #[async_trait] diff --git a/automation_devices/src/hue_switch.rs b/automation_devices/src/hue_switch.rs index 23f6bfd..e456463 100644 --- a/automation_devices/src/hue_switch.rs +++ b/automation_devices/src/hue_switch.rs @@ -4,7 +4,7 @@ use automation_lib::config::{InfoConfig, MqttDeviceConfig}; use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::OnMqtt; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::{Publish, matches}; use serde::Deserialize; use tracing::{debug, trace, warn}; @@ -51,11 +51,10 @@ struct State { action: Action, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct HueSwitch { config: Config, } -impl_device!(HueSwitch); impl Device for HueSwitch { fn get_id(&self) -> String { diff --git a/automation_devices/src/ikea_remote.rs b/automation_devices/src/ikea_remote.rs index cd0cdcf..a3602ae 100644 --- a/automation_devices/src/ikea_remote.rs +++ b/automation_devices/src/ikea_remote.rs @@ -4,7 +4,7 @@ use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::OnMqtt; use automation_lib::messages::{RemoteAction, RemoteMessage}; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use axum::async_trait; use rumqttc::{Publish, matches}; use tracing::{debug, error, trace}; @@ -27,11 +27,10 @@ pub struct Config { pub callback: ActionCallback, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct IkeaRemote { config: Config, } -impl_device!(IkeaRemote); impl Device for IkeaRemote { fn get_id(&self) -> String { diff --git a/automation_devices/src/kasa_outlet.rs b/automation_devices/src/kasa_outlet.rs index d237df1..a480213 100644 --- a/automation_devices/src/kasa_outlet.rs +++ b/automation_devices/src/kasa_outlet.rs @@ -5,7 +5,7 @@ use std::str::Utf8Error; use async_trait::async_trait; use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::OnPresence; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use bytes::{Buf, BufMut}; use google_home::errors::{self, DeviceError}; use google_home::traits::OnOff; @@ -22,11 +22,11 @@ pub struct Config { pub addr: SocketAddr, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] +#[traits(OnOff)] pub struct KasaOutlet { config: Config, } -impl_device!(KasaOutlet); #[async_trait] impl LuaDeviceCreate for KasaOutlet { diff --git a/automation_devices/src/light_sensor.rs b/automation_devices/src/light_sensor.rs index 4f82b0e..66e93cc 100644 --- a/automation_devices/src/light_sensor.rs +++ b/automation_devices/src/light_sensor.rs @@ -6,7 +6,7 @@ use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::{self, Event, EventChannel, OnMqtt}; use automation_lib::messages::BrightnessMessage; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use tracing::{debug, trace, warn}; @@ -31,12 +31,11 @@ pub struct State { is_dark: bool, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct LightSensor { config: Config, state: Arc>, } -impl_device!(LightSensor); impl LightSensor { async fn state(&self) -> RwLockReadGuard<'_, State> { diff --git a/automation_devices/src/wake_on_lan.rs b/automation_devices/src/wake_on_lan.rs index 7c5ad9c..5eb2f00 100644 --- a/automation_devices/src/wake_on_lan.rs +++ b/automation_devices/src/wake_on_lan.rs @@ -6,7 +6,7 @@ use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::OnMqtt; use automation_lib::messages::ActivateMessage; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use eui48::MacAddress; use google_home::device; use google_home::errors::ErrorCode; @@ -28,11 +28,10 @@ pub struct Config { pub client: WrappedAsyncClient, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct WakeOnLAN { config: Config, } -impl_device!(WakeOnLAN); #[async_trait] impl LuaDeviceCreate for WakeOnLAN { diff --git a/automation_devices/src/washer.rs b/automation_devices/src/washer.rs index f72b9bd..f6d5a91 100644 --- a/automation_devices/src/washer.rs +++ b/automation_devices/src/washer.rs @@ -7,7 +7,7 @@ use automation_lib::event::{self, Event, EventChannel, OnMqtt}; use automation_lib::messages::PowerMessage; use automation_lib::mqtt::WrappedAsyncClient; use automation_lib::ntfy::{Notification, Priority}; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use tracing::{debug, error, trace, warn}; @@ -31,12 +31,11 @@ pub struct State { } // TODO: Add google home integration -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct Washer { config: Config, state: Arc>, } -impl_device!(Washer); impl Washer { async fn state(&self) -> RwLockReadGuard<'_, State> { diff --git a/automation_devices/src/zigbee/light.rs b/automation_devices/src/zigbee/light.rs index 8e4a831..64157dd 100644 --- a/automation_devices/src/zigbee/light.rs +++ b/automation_devices/src/zigbee/light.rs @@ -10,7 +10,7 @@ use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::{OnMqtt, OnPresence}; use automation_lib::helpers::serialization::state_deserializer; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::device; use google_home::errors::ErrorCode; use google_home::traits::{Brightness, Color, ColorSetting, ColorTemperatureRange, OnOff}; @@ -88,7 +88,10 @@ impl From for StateBrightness { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] +#[traits(: OnOff)] +#[traits(: OnOff, Brightness)] +#[traits(: OnOff, Brightness, ColorSetting)] pub struct Light { config: Config, @@ -96,11 +99,8 @@ pub struct Light { } pub type LightOnOff = Light; -impl_device!(LightOnOff -> OnOff); pub type LightBrightness = Light; -impl_device!(LightBrightness -> OnOff, Brightness); pub type LightColorTemperature = Light; -impl_device!(LightColorTemperature -> OnOff, Brightness, ColorSetting); impl Light { async fn state(&self) -> RwLockReadGuard<'_, T> { diff --git a/automation_devices/src/zigbee/outlet.rs b/automation_devices/src/zigbee/outlet.rs index 732bade..3b69cca 100644 --- a/automation_devices/src/zigbee/outlet.rs +++ b/automation_devices/src/zigbee/outlet.rs @@ -10,7 +10,7 @@ use automation_lib::device::{Device, LuaDeviceCreate}; use automation_lib::event::{OnMqtt, OnPresence}; use automation_lib::helpers::serialization::state_deserializer; use automation_lib::mqtt::WrappedAsyncClient; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use google_home::device; use google_home::errors::ErrorCode; use google_home::traits::OnOff; @@ -84,7 +84,9 @@ impl From for StateOnOff { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] +#[traits(: OnOff)] +#[traits(: OnOff)] pub struct Outlet { config: Config, @@ -92,9 +94,7 @@ pub struct Outlet { } pub type OutletOnOff = Outlet; -impl_device!(OutletOnOff -> OnOff); pub type OutletPower = Outlet; -impl_device!(OutletPower -> OnOff); impl Outlet { async fn state(&self) -> RwLockReadGuard<'_, T> { diff --git a/automation_lib/src/ntfy.rs b/automation_lib/src/ntfy.rs index 55c8e13..a27c8a2 100644 --- a/automation_lib/src/ntfy.rs +++ b/automation_lib/src/ntfy.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::convert::Infallible; use async_trait::async_trait; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use serde::Serialize; use serde_repr::*; use tracing::{error, trace, warn}; @@ -119,11 +119,10 @@ pub struct Config { pub tx: event::Sender, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct Ntfy { config: Config, } -impl_device!(Ntfy); #[async_trait] impl LuaDeviceCreate for Ntfy { diff --git a/automation_lib/src/presence.rs b/automation_lib/src/presence.rs index 2d96bf1..b246c82 100644 --- a/automation_lib/src/presence.rs +++ b/automation_lib/src/presence.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use std::sync::Arc; use async_trait::async_trait; -use automation_macro::{LuaDeviceConfig, impl_device}; +use automation_macro::{LuaDevice, LuaDeviceConfig}; use rumqttc::Publish; use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use tracing::{debug, trace, warn}; @@ -31,12 +31,11 @@ pub struct State { current_overall_presence: bool, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, LuaDevice)] pub struct Presence { config: Config, state: Arc>, } -impl_device!(Presence); impl Presence { async fn state(&self) -> RwLockReadGuard<'_, State> { diff --git a/automation_macro/src/impl_device.rs b/automation_macro/src/impl_device.rs index 0a08960..f3c3d67 100644 --- a/automation_macro/src/impl_device.rs +++ b/automation_macro/src/impl_device.rs @@ -1,70 +1,88 @@ -use proc_macro::TokenStream; -use quote::quote; +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; use syn::parse::Parse; use syn::punctuated::Punctuated; -use syn::{Path, Token, parse_macro_input}; +use syn::{AngleBracketedGenericArguments, Attribute, DeriveInput, Ident, Path, Token}; -struct ImplDevice { - ty: Path, - impls: Option>, +#[derive(Debug, Default)] +struct Impl { + generics: Option, + traits: Vec, } -impl Parse for ImplDevice { +impl Parse for Impl { fn parse(input: syn::parse::ParseStream) -> syn::Result { - let ty = input.parse()?; - let impls = if input.peek(Token![->]) { - input.parse::]>()?; - Some(input.parse_terminated(Path::parse, Token![,])?) + let generics = if input.peek(Token![<]) { + let generics = input.parse()?; + input.parse::()?; + + Some(generics) } else { None }; - Ok(ImplDevice { ty, impls }) + let traits: Punctuated<_, _> = input.parse_terminated(Path::parse, Token![,])?; + let traits = traits.into_iter().collect(); + + Ok(Impl { generics, traits }) } } -pub fn impl_device_macro(input: proc_macro::TokenStream) -> TokenStream { - let ImplDevice { ty, impls } = parse_macro_input!(input as ImplDevice); +impl Impl { + fn generate(&self, name: &Ident) -> TokenStream { + let generics = &self.generics; - let impls: Vec<_> = impls - .iter() - .flatten() - .map(|i| { - let ident = i - .segments - .last() - .expect("There should be at least one segment") - .ident - .clone(); - - quote! { - ::automation_lib::lua::traits::#ident::add_methods(methods); + // If an identifier is specified, assume it is placed in ::automation_lib::lua::traits, + // otherwise use the provided path + let traits = self.traits.iter().map(|t| { + if let Some(ident) = t.get_ident() { + quote! {::automation_lib::lua::traits::#ident } + } else { + t.to_token_stream() } - }) - .collect(); + }); - quote! { - impl mlua::UserData for #ty { - fn add_methods>(methods: &mut M) { - methods.add_async_function("new", |_lua, config| async { - let device: #ty = LuaDeviceCreate::create(config) - .await - .map_err(mlua::ExternalError::into_lua_err)?; + quote! { + impl mlua::UserData for #name #generics { + fn add_methods>(methods: &mut M) { + methods.add_async_function("new", |_lua, config| async { + let device: Self = LuaDeviceCreate::create(config) + .await + .map_err(mlua::ExternalError::into_lua_err)?; - Ok(device) - }); + Ok(device) + }); - methods.add_method("__box", |_lua, this, _: ()| { - let b: Box = Box::new(this.clone()); - Ok(b) - }); + 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()) }); + methods.add_async_method("get_id", |_lua, this, _: ()| async move { Ok(this.get_id()) }); - #( - #impls - )* + #( + #traits::add_methods(methods); + )* + } } } - }.into() + } +} + +pub fn impl_device_macro(ast: &DeriveInput) -> TokenStream { + let name = &ast.ident; + + let impls: TokenStream = ast + .attrs + .iter() + .filter(|attr| attr.path().is_ident("traits")) + .flat_map(Attribute::parse_args::) + .map(|im| im.generate(name)) + .collect(); + + if impls.is_empty() { + Impl::default().generate(name) + } else { + impls + } } diff --git a/automation_macro/src/lib.rs b/automation_macro/src/lib.rs index f7e9dfc..17b28eb 100644 --- a/automation_macro/src/lib.rs +++ b/automation_macro/src/lib.rs @@ -14,7 +14,9 @@ pub fn lua_device_config_derive(input: proc_macro::TokenStream) -> proc_macro::T impl_lua_device_config_macro(&ast).into() } -#[proc_macro] +#[proc_macro_derive(LuaDevice, attributes(traits))] pub fn impl_device(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - impl_device_macro(input) + let ast = parse_macro_input!(input as DeriveInput); + + impl_device_macro(&ast).into() }