rmk_macro/lib.rs
1mod codegen;
2mod event;
3mod event_macros;
4mod processor;
5mod utils;
6
7use codegen::split::peripheral::parse_split_peripheral_mod;
8use darling::FromMeta;
9use darling::ast::NestedMeta;
10use proc_macro::TokenStream;
11use syn::parse_macro_input;
12
13use crate::codegen::parse_keyboard_mod;
14
15/// Expand a directory of simulator scenario TOMLs into `#[test]` fns targeting
16/// rmk's `tests/integration/simulator` harness. Test-only; see
17/// `rmk/tests/scenarios/README.md`.
18#[cfg(feature = "_simulator")]
19#[doc(hidden)]
20#[proc_macro]
21pub fn run_tests(input: TokenStream) -> TokenStream {
22 let dir = parse_macro_input!(input as syn::LitStr);
23 codegen::simulator::expand_run_tests(dir).into()
24}
25
26#[proc_macro_attribute]
27pub fn rmk_keyboard(_attr: TokenStream, item: TokenStream) -> TokenStream {
28 let item_mod = parse_macro_input!(item as syn::ItemMod);
29 parse_keyboard_mod(item_mod).into()
30}
31
32#[proc_macro_attribute]
33pub fn rmk_central(_attr: TokenStream, item: TokenStream) -> TokenStream {
34 let item_mod = parse_macro_input!(item as syn::ItemMod);
35 parse_keyboard_mod(item_mod).into()
36}
37
38/// Attribute for `rmk_peripheral` macro
39#[derive(Debug, FromMeta)]
40struct PeripheralAttr {
41 #[darling(default)]
42 id: usize,
43}
44
45#[proc_macro_attribute]
46pub fn rmk_peripheral(attr: TokenStream, item: TokenStream) -> TokenStream {
47 let item_mod = parse_macro_input!(item as syn::ItemMod);
48 let attr_args = match NestedMeta::parse_meta_list(attr.clone().into()) {
49 Ok(v) => v,
50 Err(e) => {
51 return TokenStream::from(darling::Error::from(e).write_errors());
52 }
53 };
54
55 let peripheral_id = match PeripheralAttr::from_list(&attr_args) {
56 Ok(v) => v.id,
57 Err(e) => {
58 return TokenStream::from(e.write_errors());
59 }
60 };
61
62 parse_split_peripheral_mod(peripheral_id, attr, item_mod).into()
63}
64
65/// Marker attribute for coordinating Runnable generation between macros.
66/// Do not use directly.
67#[doc(hidden)]
68#[proc_macro_attribute]
69pub fn runnable_generated(_attr: TokenStream, item: TokenStream) -> TokenStream {
70 item // Pass through unchanged
71}
72
73/// Derive macro for multi-event enums that generates automatic event dispatch.
74///
75/// This macro generates:
76/// - `{EnumName}Publisher` struct implementing `AsyncEventPublisher` and `EventPublisher`
77/// - `PublishableEvent` and `AsyncPublishableEvent` trait implementations
78/// - `From<VariantType>` impls for each variant
79///
80/// Each variant is forwarded to its underlying event channel when published.
81///
82/// **Note**: You cannot subscribe to wrapper enums directly. Subscribe to the individual
83/// concrete event types (e.g., `BatteryEvent`, `PointingEvent`) instead.
84///
85/// # Example
86///
87/// ```rust,ignore
88/// #[derive(Event)]
89/// pub enum MultiSensorEvent {
90/// Battery(BatteryEvent),
91/// Pointing(PointingEvent),
92/// }
93///
94/// // Publishing: events are routed to their concrete type channels
95/// publish_event_async(MultiSensorEvent::Battery(event)).await;
96/// ```
97#[proc_macro_derive(Event)]
98pub fn event_derive(item: TokenStream) -> TokenStream {
99 event_macros::input_event_derive::event_derive_impl(item)
100}
101
102/// Macro for defining input devices that publish events.
103///
104/// This macro generates `InputDevice` and `Runnable` implementations for single-event devices.
105/// For multi-event devices, use `#[derive(Event)]` on a user-defined enum instead.
106///
107/// # Parameters
108///
109/// - `publish`: The event type to publish (single event type only)
110///
111/// # Example
112///
113/// ```rust,ignore
114/// #[input_device(publish = BatteryEvent)]
115/// pub struct BatteryReader { ... }
116///
117/// impl BatteryReader {
118/// // User implements this inherent method
119/// async fn read_battery_event(&mut self) -> BatteryEvent {
120/// // Wait and return single event
121/// }
122/// }
123/// ```
124#[proc_macro_attribute]
125pub fn input_device(attr: TokenStream, item: TokenStream) -> TokenStream {
126 event_macros::input_device::input_device_impl(attr, item)
127}
128
129/// Unified macro for defining events with static channels.
130///
131/// Generates `PublishableEvent`, `SubscribableEvent`, and `AsyncPublishableEvent`
132/// trait implementations.
133///
134/// # Parameters
135///
136/// - `channel_size`: Buffer size of the channel (default: 8 for MPSC, 1 for PubSub)
137/// - `subs`: Max subscribers (triggers PubSub mode, default: 4)
138/// - `pubs`: Max publishers (triggers PubSub mode, default: 1)
139///
140/// If `subs` or `pubs` is specified, PubSub channel is used; otherwise MPSC channel.
141///
142/// # Examples
143///
144/// ```rust,ignore
145/// // MPSC channel (single consumer)
146/// #[event(channel_size = 16)]
147/// #[derive(Clone, Copy, Debug)]
148/// pub struct KeyboardEvent { /* ... */ }
149///
150/// // PubSub channel (multiple subscribers)
151/// #[event(channel_size = 4, subs = 8, pubs = 2)]
152/// #[derive(Clone, Copy, Debug)]
153/// pub struct LedIndicatorEvent { /* ... */ }
154/// ```
155#[proc_macro_attribute]
156pub fn event(attr: TokenStream, item: TokenStream) -> TokenStream {
157 event::event_impl(attr, item)
158}
159
160/// Unified macro for defining event processors.
161///
162/// Generates `Processor` and optional `PollingProcessor` implementations.
163///
164/// # Parameters
165///
166/// - `subscribe`: Array of event types to subscribe to (required)
167/// - `poll_interval`: Optional polling interval in milliseconds
168///
169/// # Examples
170///
171/// ```rust,ignore
172/// // Event-driven processor
173/// #[processor(subscribe = [LedIndicatorEvent])]
174/// struct LedController { /* ... */ }
175///
176/// impl LedController {
177/// async fn on_led_indicator_event(&mut self, event: LedIndicatorEvent) {
178/// // Handle event
179/// }
180/// }
181///
182/// // Polling processor
183/// #[processor(subscribe = [BatteryStatusEvent], poll_interval = 1000)]
184/// struct BatteryMonitor { /* ... */ }
185///
186/// impl BatteryMonitor {
187/// async fn on_battery_status_event(&mut self, event: BatteryStatusEvent) {
188/// // Handle event
189/// }
190///
191/// async fn poll(&mut self) {
192/// // Called every 1000ms
193/// }
194/// }
195/// ```
196#[proc_macro_attribute]
197pub fn processor(attr: TokenStream, item: TokenStream) -> TokenStream {
198 processor::processor_impl(attr, item)
199}