Compare commits

..

1 Commits

Author SHA1 Message Date
88a1c15f39 feat: WIP
All checks were successful
Build and deploy / build (push) Successful in 10m43s
Build and deploy / Deploy container (push) Has been skipped
2025-09-17 00:17:31 +02:00
4 changed files with 32 additions and 30 deletions

View File

@@ -89,9 +89,9 @@ impl From<StateColorTemperature> for StateBrightness {
} }
#[derive(Debug, Clone, Device)] #[derive(Debug, Clone, Device)]
#[device(traits(OnOff for LightOnOff, LightBrightness, LightColorTemperature))] #[device(traits(OnOff for <StateOnOff>, <StateBrightness>, <StateColorTemperature>))]
#[device(traits(Brightness for LightBrightness, LightColorTemperature))] #[device(traits(Brightness for <StateBrightness>, <StateColorTemperature>))]
#[device(traits(ColorSetting for LightColorTemperature))] #[device(traits(ColorSetting for <StateColorTemperature>))]
pub struct Light<T: LightState> { pub struct Light<T: LightState> {
config: Config<T>, config: Config<T>,
@@ -144,7 +144,7 @@ impl<T: LightState> Device for Light<T> {
} }
#[async_trait] #[async_trait]
impl OnMqtt for LightOnOff { impl OnMqtt for Light<StateOnOff> {
async fn on_mqtt(&self, message: Publish) { async fn on_mqtt(&self, message: Publish) {
// Check if the message is from the device itself or from a remote // Check if the message is from the device itself or from a remote
if matches(&message.topic, &self.config.mqtt.topic) { if matches(&message.topic, &self.config.mqtt.topic) {
@@ -177,7 +177,7 @@ impl OnMqtt for LightOnOff {
} }
#[async_trait] #[async_trait]
impl OnMqtt for LightBrightness { impl OnMqtt for Light<StateBrightness> {
async fn on_mqtt(&self, message: Publish) { async fn on_mqtt(&self, message: Publish) {
// Check if the message is from the deviec itself or from a remote // Check if the message is from the deviec itself or from a remote
if matches(&message.topic, &self.config.mqtt.topic) { if matches(&message.topic, &self.config.mqtt.topic) {
@@ -216,7 +216,7 @@ impl OnMqtt for LightBrightness {
} }
#[async_trait] #[async_trait]
impl OnMqtt for LightColorTemperature { impl OnMqtt for Light<StateColorTemperature> {
async fn on_mqtt(&self, message: Publish) { async fn on_mqtt(&self, message: Publish) {
// Check if the message is from the deviec itself or from a remote // Check if the message is from the deviec itself or from a remote
if matches(&message.topic, &self.config.mqtt.topic) { if matches(&message.topic, &self.config.mqtt.topic) {

View File

@@ -81,7 +81,7 @@ impl From<StatePower> for StateOnOff {
} }
#[derive(Debug, Clone, Device)] #[derive(Debug, Clone, Device)]
#[device(traits(OnOff for OutletOnOff, OutletPower))] #[device(traits(OnOff for <StateOnOff>, <StatePower>))]
pub struct Outlet<T: OutletState> { pub struct Outlet<T: OutletState> {
config: Config<T>, config: Config<T>,
@@ -131,7 +131,7 @@ impl<T: OutletState> Device for Outlet<T> {
} }
#[async_trait] #[async_trait]
impl OnMqtt for OutletOnOff { impl OnMqtt for Outlet<StateOnOff> {
async fn on_mqtt(&self, message: Publish) { async fn on_mqtt(&self, message: Publish) {
// Check if the message is from the device itself or from a remote // Check if the message is from the device itself or from a remote
if matches(&message.topic, &self.config.mqtt.topic) { if matches(&message.topic, &self.config.mqtt.topic) {
@@ -164,7 +164,7 @@ impl OnMqtt for OutletOnOff {
} }
#[async_trait] #[async_trait]
impl OnMqtt for OutletPower { impl OnMqtt for Outlet<StatePower> {
async fn on_mqtt(&self, message: Publish) { async fn on_mqtt(&self, message: Publish) {
// Check if the message is from the deviec itself or from a remote // Check if the message is from the deviec itself or from a remote
if matches(&message.topic, &self.config.mqtt.topic) { if matches(&message.topic, &self.config.mqtt.topic) {

View File

@@ -35,14 +35,14 @@ impl Parse for Attr {
struct TraitAttr { struct TraitAttr {
traits: Traits, traits: Traits,
aliases: Aliases, generics: Generics,
} }
impl Parse for TraitAttr { impl Parse for TraitAttr {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self { Ok(Self {
traits: input.parse()?, traits: input.parse()?,
aliases: input.parse()?, generics: input.parse()?,
}) })
} }
} }
@@ -78,15 +78,15 @@ impl ToTokens for Traits {
} }
#[derive(Default)] #[derive(Default)]
struct Aliases(Vec<syn::Ident>); struct Generics(Vec<syn::AngleBracketedGenericArguments>);
impl Aliases { impl Generics {
fn has_aliases(&self) -> bool { fn has_generics(&self) -> bool {
!self.0.is_empty() !self.0.is_empty()
} }
} }
impl Parse for Aliases { impl Parse for Generics {
fn parse(input: ParseStream) -> syn::Result<Self> { fn parse(input: ParseStream) -> syn::Result<Self> {
if !input.peek(Token![for]) { if !input.peek(Token![for]) {
if input.is_empty() { if input.is_empty() {
@@ -100,7 +100,7 @@ impl Parse for Aliases {
input input
.call(Punctuated::<_, Token![,]>::parse_separated_nonempty) .call(Punctuated::<_, Token![,]>::parse_separated_nonempty)
.map(|aliases| aliases.into_iter().collect()) .map(|generics| generics.into_iter().collect())
.map(Self) .map(Self)
} }
} }
@@ -125,7 +125,7 @@ impl ToTokens for AddMethodsAttr {
} }
struct Implementation { struct Implementation {
name: syn::Ident, generics: Option<syn::AngleBracketedGenericArguments>,
traits: Traits, traits: Traits,
add_methods: Vec<AddMethodsAttr>, add_methods: Vec<AddMethodsAttr>,
} }
@@ -133,13 +133,13 @@ struct Implementation {
impl quote::ToTokens for Implementation { impl quote::ToTokens for Implementation {
fn to_tokens(&self, tokens: &mut TokenStream2) { fn to_tokens(&self, tokens: &mut TokenStream2) {
let Self { let Self {
name, generics,
traits, traits,
add_methods, add_methods,
} = &self; } = &self;
tokens.extend(quote! { tokens.extend(quote! {
impl mlua::UserData for #name { #generics {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) { fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_function("new", async |_lua, config| { methods.add_async_function("new", async |_lua, config| {
let device: Self = LuaDeviceCreate::create(config) let device: Self = LuaDeviceCreate::create(config)
@@ -169,18 +169,18 @@ impl quote::ToTokens for Implementation {
struct Implementations(Vec<Implementation>); struct Implementations(Vec<Implementation>);
impl Implementations { impl From<Vec<Attr>> for Implementations {
fn from_attr(attributes: Vec<Attr>, name: syn::Ident) -> Self { fn from(attributes: Vec<Attr>) -> Self {
let mut add_methods = Vec::new(); let mut add_methods = Vec::new();
let mut all = Traits::default(); let mut all = Traits::default();
let mut implementations: HashMap<_, Traits> = HashMap::new(); let mut implementations: HashMap<_, Traits> = HashMap::new();
for attribute in attributes { for attribute in attributes {
match attribute { match attribute {
Attr::Trait(attribute) => { Attr::Trait(attribute) => {
if attribute.aliases.has_aliases() { if attribute.generics.has_generics() {
for alias in &attribute.aliases.0 { for generic in &attribute.generics.0 {
implementations implementations
.entry(Some(alias.clone())) .entry(Some(generic.clone()))
.or_default() .or_default()
.extend(&attribute.traits); .extend(&attribute.traits);
} }
@@ -203,8 +203,8 @@ impl Implementations {
Self( Self(
implementations implementations
.into_iter() .into_iter()
.map(|(alias, traits)| Implementation { .map(|(generics, traits)| Implementation {
name: alias.unwrap_or(name.clone()), generics,
traits, traits,
add_methods: add_methods.clone(), add_methods: add_methods.clone(),
}) })
@@ -213,7 +213,9 @@ impl Implementations {
} }
} }
pub fn device(input: DeriveInput) -> TokenStream2 { pub fn device(input: &DeriveInput) -> TokenStream2 {
let name = &input.ident;
let Implementations(imp) = match input let Implementations(imp) = match input
.attrs .attrs
.iter() .iter()
@@ -221,13 +223,13 @@ pub fn device(input: DeriveInput) -> TokenStream2 {
.map(Attribute::parse_args) .map(Attribute::parse_args)
.try_collect::<Vec<_>>() .try_collect::<Vec<_>>()
{ {
Ok(attr) => Implementations::from_attr(attr, input.ident), Ok(result) => result.into(),
Err(err) => return err.into_compile_error(), Err(err) => return err.into_compile_error(),
}; };
quote! { quote! {
#( #(
#imp impl mlua::UserData for #name #imp
)* )*
} }
} }

View File

@@ -69,5 +69,5 @@ pub fn lua_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream
#[proc_macro_derive(Device, attributes(device))] #[proc_macro_derive(Device, attributes(device))]
pub fn device(input: proc_macro::TokenStream) -> proc_macro::TokenStream { pub fn device(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ast = parse_macro_input!(input as DeriveInput); let ast = parse_macro_input!(input as DeriveInput);
device::device(ast).into() device::device(&ast).into()
} }