use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{DeriveInput, Meta, parse_macro_input};
use super::config::InputDeviceConfig;
use super::parser::parse_input_device_config;
use super::runnable::generate_runnable;
use super::utils::{attr_matches_name, deduplicate_type_generics, has_runnable_marker};
use crate::processor::{ProcessorConfig, parse_processor_config};
use crate::utils::to_snake_case;
pub fn input_device_impl(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(item as DeriveInput);
let device_config = match parse_input_device_config(proc_macro2::TokenStream::from(attr)) {
Ok(config) => config,
Err(err) => return err.into(),
};
let event_type = device_config.event_type;
if !matches!(input.data, syn::Data::Struct(_)) {
return syn::Error::new_spanned(&input, "#[input_device] can only be applied to structs")
.to_compile_error()
.into();
}
let has_marker = has_runnable_marker(&input.attrs);
let has_processor = input
.attrs
.iter()
.any(|attr| attr_matches_name(attr, "processor"));
let processor_config: Option<ProcessorConfig> = if has_processor {
let attr = input
.attrs
.iter()
.find(|attr| attr_matches_name(attr, "processor"))
.unwrap();
if let Meta::List(meta_list) = &attr.meta {
match parse_processor_config(meta_list.tokens.clone()) {
Ok(config) => Some(config),
Err(err) => return err.into(),
}
} else {
return syn::Error::new_spanned(
attr,
"#[processor] requires parameters. Use `#[processor(subscribe = [EventType])]`",
)
.to_compile_error()
.into();
}
} else {
None
};
let struct_name = &input.ident;
let generics = &input.generics;
let (impl_generics, _ty_generics, where_clause) = generics.split_for_impl();
let deduped_ty_generics = deduplicate_type_generics(generics);
let type_name = event_type.segments.last().unwrap().ident.to_string();
let base_name = type_name.strip_suffix("Event").unwrap_or(&type_name);
let method_name = format_ident!("read_{}_event", to_snake_case(base_name));
let (runnable_impl, generated_runnable) = if has_marker {
(quote! {}, false)
} else {
let input_device_cfg = InputDeviceConfig {
event_type: event_type.clone(),
};
(
generate_runnable(
struct_name,
generics,
where_clause,
Some(&input_device_cfg),
processor_config.as_ref(),
),
true,
)
};
input
.attrs
.retain(|attr| !attr.path().is_ident("input_device"));
if has_processor && !has_marker && generated_runnable {
input.attrs.push(syn::parse_quote!(
#[::rmk::macros::runnable_generated]
));
}
let expanded = quote! {
#input
impl #impl_generics ::rmk::input_device::InputDevice for #struct_name #deduped_ty_generics #where_clause {
type Event = #event_type;
async fn read_event(&mut self) -> Self::Event {
self.#method_name().await
}
}
#runnable_impl
};
expanded.into()
}