From a22819c864265670c5fdc7680e483770f22bcc20 Mon Sep 17 00:00:00 2001 From: Soispha Date: Sun, 23 Jul 2023 16:35:18 +0200 Subject: [PATCH] Fix(app::command_interface): Provide pre-generated file, to check syntax --- .../app/command_interface.lua_macros.rs | 49 ++++++++ src/tui_app/app/command_interface.rs | 108 ++++++++++-------- 2 files changed, 110 insertions(+), 47 deletions(-) create mode 100644 src/tui_app/app/command_interface.lua_macros.rs diff --git a/src/tui_app/app/command_interface.lua_macros.rs b/src/tui_app/app/command_interface.lua_macros.rs new file mode 100644 index 0000000..6f74395 --- /dev/null +++ b/src/tui_app/app/command_interface.lua_macros.rs @@ -0,0 +1,49 @@ +// FIXME: This file needs documentation with examples of how the proc macros work. +// for now use `cargo expand app::command_interface` for an overview + +use std::{io::{Error, ErrorKind}, sync::Arc}; + +use lua_macros::{ci_command, turn_struct_to_ci_command_enum}; + +use crate::app::event_types::Event; +/// This struct is here to guarantee, that all functions actually end up in the lua context. +/// I.e. Rust should throw a compile error, when one field is added, but not a matching function. +/// +/// What it does: +/// - Generates a `generate_ci_functions` function, which wraps the specified rust in functions +/// in lua and exports them to the globals in the context provided as argument. +/// - Generates a Commands enum, which contains every Camel cased version of the fields. +/// +/// Every command specified here should have a function named $command_name, where $command_name is the snake cased name of the field. +/// +/// This function is exported to the lua context, thus it's signature must be: +/// ```rust +/// fn $command_name(context: Context, input_string: String) -> Result<$return_type, rlua::Error> {} +/// ``` +/// where $return_type is the type returned by the function (the only supported ones are right now +/// `String` and `()`). + +#[turn_struct_to_ci_command_enum] +struct Commands { + /// Greets the user + greet: fn(String) -> String, + + /// Closes the application + //#[expose(lua)] + exit: fn(), + + /// Shows the command line + command_line_show: fn(), + + /// Hides the command line + command_line_hide: fn(), + + /// Go to the next plane + cycle_planes: fn(), + /// Go to the previous plane + cycle_planes_rev: fn(), + + /// Send a message to the current room + /// The send message is interpreted literally. + room_message_send: fn(String) -> String, +} diff --git a/src/tui_app/app/command_interface.rs b/src/tui_app/app/command_interface.rs index 6f74395..59a9740 100644 --- a/src/tui_app/app/command_interface.rs +++ b/src/tui_app/app/command_interface.rs @@ -1,49 +1,63 @@ -// FIXME: This file needs documentation with examples of how the proc macros work. -// for now use `cargo expand app::command_interface` for an overview +use cli_log::debug; -use std::{io::{Error, ErrorKind}, sync::Arc}; - -use lua_macros::{ci_command, turn_struct_to_ci_command_enum}; - -use crate::app::event_types::Event; -/// This struct is here to guarantee, that all functions actually end up in the lua context. -/// I.e. Rust should throw a compile error, when one field is added, but not a matching function. -/// -/// What it does: -/// - Generates a `generate_ci_functions` function, which wraps the specified rust in functions -/// in lua and exports them to the globals in the context provided as argument. -/// - Generates a Commands enum, which contains every Camel cased version of the fields. -/// -/// Every command specified here should have a function named $command_name, where $command_name is the snake cased name of the field. -/// -/// This function is exported to the lua context, thus it's signature must be: -/// ```rust -/// fn $command_name(context: Context, input_string: String) -> Result<$return_type, rlua::Error> {} -/// ``` -/// where $return_type is the type returned by the function (the only supported ones are right now -/// `String` and `()`). - -#[turn_struct_to_ci_command_enum] -struct Commands { - /// Greets the user - greet: fn(String) -> String, - - /// Closes the application - //#[expose(lua)] - exit: fn(), - - /// Shows the command line - command_line_show: fn(), - - /// Hides the command line - command_line_hide: fn(), - - /// Go to the next plane - cycle_planes: fn(), - /// Go to the previous plane - cycle_planes_rev: fn(), - - /// Send a message to the current room - /// The send message is interpreted literally. - room_message_send: fn(String) -> String, +#[derive(Debug)] +pub enum Command { + Greet(String), + Exit, + CommandLineShow, + CommandLineHide, + CyclePlanes, + CyclePlanesRev, + RoomMessageSend(String), + Help(Option), +} + +pub fn generate_ci_functions( + lua: mlua::Lua, + tx: tokio::sync::mpsc::Sender, +) -> mlua::Lua { + lua.set_app_data(tx); + let globals = lua.globals(); + let fun_greet = lua.create_async_function(greet).expect(&{ + let res = format!("The function: `{}` should be defined", "greet"); + res + }); + globals.set("greet", fun_greet).expect(&{ + let res = format!( + "Setting a static global value ({}, fun_{}) should work", + "greet", "greet", + ); + res + }); + + drop(globals); + lua +} + +async fn greet(lua: &mlua::Lua, input: String) -> Result { + let (callback_tx, mut callback_rx) = tokio::sync::mpsc::channel::(256); + let tx: core::cell::Ref> = + lua.app_data_ref().expect("This exists, it was set before"); + + debug!("Got tx"); + (*tx) + .try_send(crate::app::events::event_types::Event::CommandEvent( + Command::Greet(input.clone()), + Some(callback_tx), + )) + .expect("This should work, as the reciever is not dropped"); + + debug!("Sent CommandEvent"); + + debug!("Returning output..."); + if let Some(output) = callback_rx.recv().await { + callback_rx.close(); + debug!("returned output"); + return Ok(output); + } else { + debug!("Not returning output..."); + return Err(mlua::Error::ExternalError(std::sync::Arc::new( + std::io::Error::new(std::io::ErrorKind::Other, "Callback reciever dropped"), + ))); + } }