From 06b3154733bd4a841ef5c6b5b9515f7b380b0831 Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Wed, 17 Sep 2025 00:35:30 +0200 Subject: [PATCH] feat!: Use type alias instead of generic parameters in device macro This enforced the idea that all generics must be specified for the type when using the device macro. It will also come into play later when the Typed macro gets introduced, as the name will be used when generating definitions. --- automation_devices/src/zigbee/light.rs | 12 +++---- automation_devices/src/zigbee/outlet.rs | 6 ++-- automation_macro/src/device.rs | 42 ++++++++++++------------- automation_macro/src/lib.rs | 2 +- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/automation_devices/src/zigbee/light.rs b/automation_devices/src/zigbee/light.rs index 730dafa..d5ba0ed 100644 --- a/automation_devices/src/zigbee/light.rs +++ b/automation_devices/src/zigbee/light.rs @@ -89,9 +89,9 @@ impl From for StateBrightness { } #[derive(Debug, Clone, Device)] -#[device(traits(OnOff for , , ))] -#[device(traits(Brightness for , ))] -#[device(traits(ColorSetting for ))] +#[device(traits(OnOff for LightOnOff, LightBrightness, LightColorTemperature))] +#[device(traits(Brightness for LightBrightness, LightColorTemperature))] +#[device(traits(ColorSetting for LightColorTemperature))] pub struct Light { config: Config, @@ -144,7 +144,7 @@ impl Device for Light { } #[async_trait] -impl OnMqtt for Light { +impl OnMqtt for LightOnOff { async fn on_mqtt(&self, message: Publish) { // Check if the message is from the device itself or from a remote if matches(&message.topic, &self.config.mqtt.topic) { @@ -177,7 +177,7 @@ impl OnMqtt for Light { } #[async_trait] -impl OnMqtt for Light { +impl OnMqtt for LightBrightness { async fn on_mqtt(&self, message: Publish) { // Check if the message is from the deviec itself or from a remote if matches(&message.topic, &self.config.mqtt.topic) { @@ -216,7 +216,7 @@ impl OnMqtt for Light { } #[async_trait] -impl OnMqtt for Light { +impl OnMqtt for LightColorTemperature { async fn on_mqtt(&self, message: Publish) { // Check if the message is from the deviec itself or from a remote if matches(&message.topic, &self.config.mqtt.topic) { diff --git a/automation_devices/src/zigbee/outlet.rs b/automation_devices/src/zigbee/outlet.rs index 220418e..ddf640d 100644 --- a/automation_devices/src/zigbee/outlet.rs +++ b/automation_devices/src/zigbee/outlet.rs @@ -81,7 +81,7 @@ impl From for StateOnOff { } #[derive(Debug, Clone, Device)] -#[device(traits(OnOff for , ))] +#[device(traits(OnOff for OutletOnOff, OutletPower))] pub struct Outlet { config: Config, @@ -131,7 +131,7 @@ impl Device for Outlet { } #[async_trait] -impl OnMqtt for Outlet { +impl OnMqtt for OutletOnOff { async fn on_mqtt(&self, message: Publish) { // Check if the message is from the device itself or from a remote if matches(&message.topic, &self.config.mqtt.topic) { @@ -164,7 +164,7 @@ impl OnMqtt for Outlet { } #[async_trait] -impl OnMqtt for Outlet { +impl OnMqtt for OutletPower { async fn on_mqtt(&self, message: Publish) { // Check if the message is from the deviec itself or from a remote if matches(&message.topic, &self.config.mqtt.topic) { diff --git a/automation_macro/src/device.rs b/automation_macro/src/device.rs index 99058d3..b886436 100644 --- a/automation_macro/src/device.rs +++ b/automation_macro/src/device.rs @@ -35,14 +35,14 @@ impl Parse for Attr { struct TraitAttr { traits: Traits, - generics: Generics, + aliases: Aliases, } impl Parse for TraitAttr { fn parse(input: ParseStream) -> syn::Result { Ok(Self { traits: input.parse()?, - generics: input.parse()?, + aliases: input.parse()?, }) } } @@ -78,15 +78,15 @@ impl ToTokens for Traits { } #[derive(Default)] -struct Generics(Vec); +struct Aliases(Vec); -impl Generics { - fn has_generics(&self) -> bool { +impl Aliases { + fn has_aliases(&self) -> bool { !self.0.is_empty() } } -impl Parse for Generics { +impl Parse for Aliases { fn parse(input: ParseStream) -> syn::Result { if !input.peek(Token![for]) { if input.is_empty() { @@ -100,7 +100,7 @@ impl Parse for Generics { input .call(Punctuated::<_, Token![,]>::parse_separated_nonempty) - .map(|generics| generics.into_iter().collect()) + .map(|aliases| aliases.into_iter().collect()) .map(Self) } } @@ -125,7 +125,7 @@ impl ToTokens for AddMethodsAttr { } struct Implementation { - generics: Option, + name: syn::Ident, traits: Traits, add_methods: Vec, } @@ -133,13 +133,13 @@ struct Implementation { impl quote::ToTokens for Implementation { fn to_tokens(&self, tokens: &mut TokenStream2) { let Self { - generics, + name, traits, add_methods, } = &self; tokens.extend(quote! { - #generics { + impl mlua::UserData for #name { fn add_methods>(methods: &mut M) { methods.add_async_function("new", async |_lua, config| { let device: Self = LuaDeviceCreate::create(config) @@ -169,18 +169,18 @@ impl quote::ToTokens for Implementation { struct Implementations(Vec); -impl From> for Implementations { - fn from(attributes: Vec) -> Self { +impl Implementations { + fn from_attr(attributes: Vec, name: syn::Ident) -> Self { let mut add_methods = Vec::new(); let mut all = Traits::default(); let mut implementations: HashMap<_, Traits> = HashMap::new(); for attribute in attributes { match attribute { Attr::Trait(attribute) => { - if attribute.generics.has_generics() { - for generic in &attribute.generics.0 { + if attribute.aliases.has_aliases() { + for alias in &attribute.aliases.0 { implementations - .entry(Some(generic.clone())) + .entry(Some(alias.clone())) .or_default() .extend(&attribute.traits); } @@ -203,8 +203,8 @@ impl From> for Implementations { Self( implementations .into_iter() - .map(|(generics, traits)| Implementation { - generics, + .map(|(alias, traits)| Implementation { + name: alias.unwrap_or(name.clone()), traits, add_methods: add_methods.clone(), }) @@ -213,9 +213,7 @@ impl From> for Implementations { } } -pub fn device(input: &DeriveInput) -> TokenStream2 { - let name = &input.ident; - +pub fn device(input: DeriveInput) -> TokenStream2 { let Implementations(imp) = match input .attrs .iter() @@ -223,13 +221,13 @@ pub fn device(input: &DeriveInput) -> TokenStream2 { .map(Attribute::parse_args) .try_collect::>() { - Ok(result) => result.into(), + Ok(attr) => Implementations::from_attr(attr, input.ident), Err(err) => return err.into_compile_error(), }; quote! { #( - impl mlua::UserData for #name #imp + #imp )* } } diff --git a/automation_macro/src/lib.rs b/automation_macro/src/lib.rs index 3e03abe..162ff30 100644 --- a/automation_macro/src/lib.rs +++ b/automation_macro/src/lib.rs @@ -69,5 +69,5 @@ pub fn lua_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream #[proc_macro_derive(Device, attributes(device))] pub fn device(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let ast = parse_macro_input!(input as DeriveInput); - device::device(&ast).into() + device::device(ast).into() }