use darling::FromMeta;
use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use rmk_config::{BoardConfig, CommunicationConfig, KeyboardTomlConfig};
use syn::{ItemFn, ItemMod};
use crate::keyboard::Overwritten;
pub(crate) fn expand_rmk_entry(
keyboard_config: &KeyboardTomlConfig,
item_mod: &ItemMod,
devices: Vec<TokenStream2>,
processors: Vec<TokenStream2>,
controllers: Vec<TokenStream2>,
) -> TokenStream2 {
if let Some((_, items)) = &item_mod.content {
items
.iter()
.find_map(|item| {
if let syn::Item::Fn(item_fn) = &item
&& item_fn.attrs.len() == 1
&& let Ok(Overwritten::Entry) = Overwritten::from_meta(&item_fn.attrs[0].meta)
{
return Some(override_rmk_entry(item_fn));
}
None
})
.unwrap_or(rmk_entry_select(keyboard_config, devices, processors, controllers))
} else {
rmk_entry_select(keyboard_config, devices, processors, controllers)
}
}
fn override_rmk_entry(item_fn: &ItemFn) -> TokenStream2 {
let content = &item_fn.block.stmts;
quote! {
#(#content)*
}
}
pub(crate) fn rmk_entry_select(
keyboard_config: &KeyboardTomlConfig,
devices: Vec<TokenStream2>,
processors: Vec<TokenStream2>,
controllers: Vec<TokenStream2>,
) -> TokenStream2 {
let devices_task = {
let mut devs = devices.clone();
devs.push(quote! {matrix});
quote! {
::rmk::run_devices! (
(#(#devs),*) => ::rmk::channel::EVENT_CHANNEL,
)
}
};
let processors_task = if processors.is_empty() {
quote! {}
} else {
quote! {
::rmk::run_processor_chain! (
::rmk::channel::EVENT_CHANNEL=> [#(#processors),*],
)
}
};
let event_trait_import = if !controllers.is_empty() {
quote! { use ::rmk::controller::EventController; }
} else {
quote! {}
};
let storage = if keyboard_config.get_storage_config().enabled {
quote! {&mut storage,}
} else {
TokenStream2::new()
};
let keymap = if keyboard_config.get_host_config().vial_enabled {
quote! { &keymap, }
} else {
quote! {}
};
let board = keyboard_config.get_board_config().unwrap();
let communication = keyboard_config.get_communication_config().unwrap();
let usb_driver_arg = match communication {
CommunicationConfig::Usb(_) | CommunicationConfig::Both(_, _) => quote! { driver, },
CommunicationConfig::Ble(_) => quote! {},
CommunicationConfig::None => panic!("USB and BLE are both disabled"),
};
let entry = match &board {
BoardConfig::Split(split_config) => {
let keyboard_task = quote! {
keyboard.run(),
};
let mut tasks = vec![devices_task, keyboard_task];
tasks.extend(controllers);
if split_config.connection == "ble" {
let rmk_task = quote! {
::rmk::run_rmk(#keymap #usb_driver_arg &stack, #storage rmk_config)
};
tasks.push(rmk_task);
if !processors.is_empty() {
tasks.push(processors_task);
};
split_config.peripheral.iter().enumerate().for_each(|(idx, p)| {
let row = p.rows;
let col = p.cols;
let row_offset = p.row_offset;
let col_offset = p.col_offset;
tasks.push(quote! {
::rmk::split::central::run_peripheral_manager::<#row, #col, #row_offset, #col_offset, _>(
#idx,
&peripheral_addrs,
&stack,
)
});
});
let scan_task = quote! {
::rmk::split::ble::central::scan_peripherals(&stack, &peripheral_addrs)
};
tasks.push(scan_task);
join_all_tasks(tasks)
} else if split_config.connection == "serial" {
let rmk_task = quote! {
::rmk::run_rmk(#keymap #usb_driver_arg #storage rmk_config),
};
tasks.push(rmk_task);
if !processors.is_empty() {
tasks.push(processors_task);
};
let central_serials = split_config
.central
.serial
.clone()
.expect("No serial defined for central");
split_config.peripheral.iter().enumerate().for_each(|(idx, p)| {
let row = p.rows;
let col = p.cols;
let row_offset = p.row_offset;
let col_offset = p.col_offset;
let uart_instance = format_ident!(
"{}",
central_serials
.get(idx)
.expect("No or not enough serial defined for peripheral in central")
.instance
.to_lowercase()
);
tasks.push(quote! {
::rmk::split::central::run_peripheral_manager::<#row, #col, #row_offset, #col_offset, _>(
#idx,
#uart_instance,
)
});
});
join_all_tasks(tasks)
} else {
panic!(
"Invalid split connection type: {}, only \"ble\" and \"serial\" are supported",
split_config.connection
);
}
}
BoardConfig::UniBody(_) => rmk_entry_unibody(keyboard_config, devices_task, processors_task, controllers),
};
quote! {
use ::rmk::input_device::Runnable;
#event_trait_import
#entry
}
}
pub(crate) fn rmk_entry_unibody(
keyboard_config: &KeyboardTomlConfig,
devices_task: TokenStream2,
processors_task: TokenStream2,
controllers: Vec<TokenStream2>,
) -> TokenStream2 {
let keyboard_task = quote! {
keyboard.run()
};
let mut tasks = vec![devices_task, keyboard_task];
if !processors_task.is_empty() {
tasks.push(processors_task);
}
tasks.extend(controllers);
let storage = if keyboard_config.get_storage_config().enabled {
quote! {&mut storage,}
} else {
TokenStream2::new()
};
let keymap = if keyboard_config.get_host_config().vial_enabled {
quote! { &keymap, }
} else {
quote! {}
};
let communication = keyboard_config.get_communication_config().unwrap();
match communication {
CommunicationConfig::Usb(_) => {
let rmk_task = quote! {
::rmk::run_rmk(#keymap driver, #storage rmk_config)
};
tasks.push(rmk_task);
join_all_tasks(tasks)
}
CommunicationConfig::Ble(_) => {
let rmk_task = quote! {
::rmk::run_rmk(#keymap &stack, #storage rmk_config)
};
tasks.push(rmk_task);
join_all_tasks(tasks)
}
CommunicationConfig::Both(_, _) => {
let rmk_task = quote! {
::rmk::run_rmk(#keymap driver, &stack, #storage rmk_config)
};
tasks.push(rmk_task);
join_all_tasks(tasks)
}
CommunicationConfig::None => panic!("USB and BLE are both disabled"),
}
}
pub fn expand_tasks(tasks: Vec<TokenStream2>) -> TokenStream2 {
let mut current_joined = quote! {};
tasks.iter().enumerate().for_each(|(id, task)| {
if id == 0 {
current_joined = quote! {#task};
} else {
current_joined = quote! {
::rmk::embassy_futures::join::join(#current_joined, #task)
};
}
});
current_joined
}
pub(crate) fn join_all_tasks(tasks: Vec<TokenStream2>) -> TokenStream2 {
let joined = expand_tasks(tasks);
quote! {
#joined.await;
}
}