2023-10-14 10:42:36 +00:00
|
|
|
use command_enum_parsing::DataCommandEnum;
|
2023-07-26 14:59:05 +00:00
|
|
|
use proc_macro::TokenStream;
|
|
|
|
use proc_macro2::TokenStream as TokenStream2;
|
|
|
|
use quote::quote;
|
2023-10-14 10:42:36 +00:00
|
|
|
use syn::parse_macro_input;
|
|
|
|
|
2023-07-26 14:59:05 +00:00
|
|
|
|
|
|
|
mod generate;
|
2023-10-14 10:42:36 +00:00
|
|
|
mod command_enum_parsing;
|
2023-07-26 14:59:05 +00:00
|
|
|
|
|
|
|
/// This is the heart of the command api
|
|
|
|
/// It mainly does two things:
|
|
|
|
/// - Generate a command enum
|
|
|
|
/// - Wrap the enum in all supported languages (only lua for now)
|
|
|
|
/// - Generate wrapper lua function for each command
|
|
|
|
/// - Generate a `add_lua_functions_to_globals` function, which adds
|
|
|
|
/// the rust wrapper functions to the lua globals.
|
|
|
|
///
|
|
|
|
/// The input and output values of the wrapped functions are derived from the values specified in
|
2023-09-20 17:21:44 +00:00
|
|
|
/// the input to the `parse_command_enum` proc macro.
|
2023-07-26 14:59:05 +00:00
|
|
|
///
|
|
|
|
/// For example this rust code:
|
2023-09-20 17:21:44 +00:00
|
|
|
/// ```no_run
|
|
|
|
/// parse_command_enum! {
|
2023-07-26 14:59:05 +00:00
|
|
|
/// /// Greets the user
|
|
|
|
/// greet: fn(String) -> String,
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
/// results in this expanded code:
|
2023-09-20 17:21:44 +00:00
|
|
|
/// ```no_run
|
2023-07-26 14:59:05 +00:00
|
|
|
/// #[derive(Debug)]
|
|
|
|
/// pub enum Command {
|
|
|
|
/// Greet(String),
|
|
|
|
/// }
|
|
|
|
/// pub fn add_lua_functions_to_globals(
|
|
|
|
/// lua: mlua::Lua,
|
|
|
|
/// tx: tokio::sync::mpsc::Sender<Event>,
|
|
|
|
/// ) -> mlua::Lua {
|
|
|
|
/// lua.set_app_data(tx);
|
|
|
|
/// let globals = lua.globals();
|
|
|
|
/// {
|
|
|
|
/// let wrapped_lua_function_greet = lua
|
|
|
|
/// .create_async_function(greet)
|
|
|
|
/// .expect(
|
|
|
|
/// format!(
|
|
|
|
/// "The function: `{}` should be defined",
|
|
|
|
/// "greet",
|
|
|
|
/// )
|
|
|
|
/// );
|
|
|
|
/// globals
|
|
|
|
/// .set("greet", wrapped_lua_function_greet)
|
|
|
|
/// .expect("Setting a static global value should work");
|
|
|
|
/// }
|
|
|
|
/// drop(globals);
|
|
|
|
/// lua
|
|
|
|
/// }
|
|
|
|
/// async fn greet(lua: &mlua::Lua, input: String) -> Result<String, mlua::Error> {
|
|
|
|
/// let (callback_tx, callback_rx) = tokio::sync::oneshot::channel::<String>();
|
|
|
|
/// let tx: core::cell::Ref<tokio::sync::mpsc::Sender<Event>> = lua
|
|
|
|
/// .app_data_ref()
|
|
|
|
/// .expect("This should exist, it was set before");
|
|
|
|
/// (*tx)
|
|
|
|
/// .send(Event::CommandEvent(Command::Greet(input.clone()), Some(callback_tx)))
|
|
|
|
/// .await
|
|
|
|
/// .expect("This should work, as the receiver is not dropped");
|
|
|
|
/// match callback_rx.await {
|
|
|
|
/// Ok(output) => {
|
|
|
|
/// return Ok(output);
|
|
|
|
/// }
|
|
|
|
/// Err(err) => {
|
|
|
|
/// return Err(mlua::Error::ExternalError(std::sync::Arc::new(err)));
|
|
|
|
/// }
|
|
|
|
/// };
|
|
|
|
/// }
|
|
|
|
/// ```
|
2023-09-20 17:21:44 +00:00
|
|
|
#[proc_macro]
|
|
|
|
pub fn parse_command_enum(input: TokenStream) -> TokenStream {
|
|
|
|
let input = parse_macro_input!(input as DataCommandEnum);
|
2023-07-26 14:59:05 +00:00
|
|
|
|
|
|
|
// Build the language wrappers
|
|
|
|
let lua_wrapper: TokenStream2 = generate::lua_wrapper(&input);
|
|
|
|
|
|
|
|
// Build the final enum
|
|
|
|
let command_enum = generate::command_enum(&input);
|
|
|
|
|
2023-09-20 17:21:44 +00:00
|
|
|
let output = quote! {
|
2023-07-26 14:59:05 +00:00
|
|
|
#command_enum
|
|
|
|
#lua_wrapper
|
2023-09-20 17:21:44 +00:00
|
|
|
};
|
|
|
|
output.into()
|
2023-07-26 14:59:05 +00:00
|
|
|
}
|