From 327450c64ba8fb6a0b4ce0eb3375847025a17b72 Mon Sep 17 00:00:00 2001 From: Soispha Date: Wed, 12 Jul 2023 21:48:51 +0200 Subject: [PATCH] Feat(command_interface): Add basic lua support --- Cargo.lock | 74 +++++++++++++++---- Cargo.toml | 2 + flake.nix | 1 + lua_macros/.gitignore | 6 ++ lua_macros/Cargo.toml | 12 +++ lua_macros/src/lib.rs | 59 +++++++++++++++ src/app/command_interface.rs | 13 ++++ .../events/event_types/event/handlers/main.rs | 37 +++++++++- src/app/mod.rs | 28 +++++++ 9 files changed, 216 insertions(+), 16 deletions(-) create mode 100644 lua_macros/.gitignore create mode 100644 lua_macros/Cargo.toml create mode 100644 lua_macros/src/lib.rs create mode 100644 src/app/command_interface.rs diff --git a/Cargo.lock b/Cargo.lock index 775b5e7..1c4c9f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -140,7 +140,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -151,7 +151,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -269,6 +269,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -642,7 +651,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -841,7 +850,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -1278,6 +1287,15 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "lua_macros" +version = "0.1.0" +dependencies = [ + "proc-macro2 1.0.64", + "quote 1.0.29", + "syn 2.0.25", +] + [[package]] name = "maplit" version = "1.0.2" @@ -1586,7 +1604,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -1701,7 +1719,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -2097,6 +2115,30 @@ dependencies = [ "winreg", ] +[[package]] +name = "rlua" +version = "0.19.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d33e5ba15c3d43178f283ed5863d4531e292fc0e56fb773f3bea45f18e3a42a" +dependencies = [ + "bitflags", + "bstr", + "libc", + "num-traits", + "rlua-lua54-sys", +] + +[[package]] +name = "rlua-lua54-sys" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aafabafe1895cb4a2be81a56d7ff3d46bf4b5d2f9cfdbea2ed404cdabe96474" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "ruma" version = "0.7.4" @@ -2286,7 +2328,7 @@ checksum = "27738cfea0d944ab72c3ed01f3d5f23ec4322af8a1431e40ce630e4c01ea74fd" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -2458,9 +2500,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.24" +version = "2.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ccaf716a23c35ff908f91c971a86a9a71af5998c1d8f10e828d9f55f68ac00" +checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", @@ -2498,7 +2540,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -2553,7 +2595,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -2632,7 +2674,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] [[package]] @@ -2652,7 +2694,9 @@ dependencies = [ "cli-log", "crossterm", "indexmap 2.0.0", + "lua_macros", "matrix-sdk", + "rlua", "serde", "tokio", "tokio-util", @@ -2870,7 +2914,7 @@ dependencies = [ "once_cell", "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", "wasm-bindgen-shared", ] @@ -2904,7 +2948,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3090,5 +3134,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2 1.0.64", "quote 1.0.29", - "syn 2.0.24", + "syn 2.0.25", ] diff --git a/Cargo.toml b/Cargo.toml index 92db0f4..55e4b6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +lua_macros = { path = "./lua_macros" } tui = "0.19" tui-textarea = { version = "0.2", features = ["crossterm"] } crossterm = "0.25" @@ -17,3 +18,4 @@ tokio-util = "0.7" serde = "1.0" cli-log = "2.0" indexmap = "2.0.0" +rlua = "0.19.7" diff --git a/flake.nix b/flake.nix index 91af5ee..72bee03 100644 --- a/flake.nix +++ b/flake.nix @@ -81,6 +81,7 @@ rust-stable rust-analyzer cargo-edit + cargo-expand ]; inherit nativeBuildInputs buildInputs; }; diff --git a/lua_macros/.gitignore b/lua_macros/.gitignore new file mode 100644 index 0000000..72fc7e3 --- /dev/null +++ b/lua_macros/.gitignore @@ -0,0 +1,6 @@ +# build +/target +/result + +# lua_macros is a library +Cargo.lock diff --git a/lua_macros/Cargo.toml b/lua_macros/Cargo.toml new file mode 100644 index 0000000..bf1583b --- /dev/null +++ b/lua_macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "lua_macros" +version = "0.1.0" +edition = "2021" + +[lib] +crate_type = ["proc-macro"] + +[dependencies] +proc-macro2 = "1.0.64" +quote = "1.0.29" +syn = "2.0.25" diff --git a/lua_macros/src/lib.rs b/lua_macros/src/lib.rs new file mode 100644 index 0000000..777665e --- /dev/null +++ b/lua_macros/src/lib.rs @@ -0,0 +1,59 @@ +use proc_macro::TokenStream; +use proc_macro2::TokenStream as TokenStream2; +use quote::{format_ident, quote}; +use syn; + +#[proc_macro_attribute] +pub fn generate_ci_functions(_: TokenStream, input: TokenStream) -> TokenStream { + // Construct a representation of Rust code as a syntax tree + // that we can manipulate + let input = syn::parse(input) + .expect("This should always be valid rust code, as it's extracted from direct code"); + + // Build the trait implementation + generate_generate_ci_functions(&input) +} + +fn generate_generate_ci_functions(input: &syn::DeriveInput) -> TokenStream { + let input_tokens: TokenStream2 = match &input.data { + syn::Data::Struct(input) => match &input.fields { + syn::Fields::Named(named_fields) => named_fields + .named + .iter() + .map(|field| -> TokenStream2 { + let field_ident = field.ident.as_ref().expect( + "These are only the named field, thus they all should have a name.", + ); + let function_name_ident = format_ident!("fun_{}", field_ident); + let function_name = format!("{}", field_ident); + quote! { + let #function_name_ident = context.create_function(#field_ident).expect( + &format!( + "The function: `{}` should be defined", + #function_name + ) + ); + globals.set(#function_name, #function_name_ident).expect( + &format!( + "Setting a static global value ({}, fun_{}) should work", + #function_name, + #function_name + ) + ); + } + .into() + }) + .collect(), + _ => unimplemented!("Only implemented for named fileds"), + }, + _ => unimplemented!("Only for implemented for structs"), + }; + + let gen = quote! { + pub fn generate_ci_functions(context: &mut Context) { + let globals = context.globals(); + #input_tokens + } + }; + gen.into() +} diff --git a/src/app/command_interface.rs b/src/app/command_interface.rs new file mode 100644 index 0000000..e38e91e --- /dev/null +++ b/src/app/command_interface.rs @@ -0,0 +1,13 @@ +use lua_macros::generate_ci_functions; +use rlua::Context; + +// This struct is here to gurantee, 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. +#[generate_ci_functions()] +struct Commands<'lua> { + greet: Function<'lua>, +} + +fn greet(context: Context, name: String) -> Result { + Ok(format!("Name is {}", name)) +} diff --git a/src/app/events/event_types/event/handlers/main.rs b/src/app/events/event_types/event/handlers/main.rs index 410e2ed..11bb804 100644 --- a/src/app/events/event_types/event/handlers/main.rs +++ b/src/app/events/event_types/event/handlers/main.rs @@ -136,7 +136,42 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result { - todo!(); + if let Some(_) = app.ui.cli { + match input { + CrosstermEvent::Key(KeyEvent { + code: KeyCode::Enter, + .. + }) => { + let cli_event = app.ui + .cli + .as_mut() + .expect("This is already checked") + .lines() + .get(0) + .expect( + "There can only be one line in the buffer, as we collect it on enter being inputted" + ) + .to_owned(); + let output = app.handle_ci_event(&cli_event).await?; + + // delete the old text: + + // We can use a mutable borrow now, as we should only need one + let cli = app.ui.cli.as_mut().expect("Checked above"); + cli.move_cursor(tui_textarea::CursorMove::Jump(0, 0)); + cli.delete_str(0, cli_event.chars().count()); + assert!(cli.is_empty()); + cli.insert_str(output); + } + _ => { + app.ui + .cli + .as_mut() + .expect("This is already checked") + .input(tui_textarea::Input::from(input.to_owned())); + } + }; + }; } _ => (), }, diff --git a/src/app/mod.rs b/src/app/mod.rs index 126b757..c30146e 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -26,10 +26,21 @@ pub struct App<'ui> { channel_rx: mpsc::Receiver, input_listener_killer: CancellationToken, matrix_listener_killer: CancellationToken, + + lua: Lua, } impl App<'_> { pub fn new() -> Result { + fn set_up_lua() -> Lua { + let lua = Lua::new(); + + lua.context(|mut lua_context| { + generate_ci_functions(&mut lua_context); + }); + lua + } + let path: &std::path::Path = Path::new("userdata/accounts.json"); let config = if path.exists() { info!("Reading account config (userdata/accounts.json)"); @@ -49,9 +60,26 @@ impl App<'_> { channel_rx, input_listener_killer: CancellationToken::new(), matrix_listener_killer: CancellationToken::new(), + + lua: set_up_lua(), }) } + pub async fn handle_ci_event(&self, event: &str) -> Result { + info!("Recieved ci event: `{event}`; executing.."); + + // TODO: Should the ci support more than strings? + let output = self.lua.context(|context| -> Result { + let output = context + .load(&event) + .eval::() + .with_context(|| format!("Failed to execute: `{event}`"))?; + info!("Function evaluated to: `{output}`"); + Ok(output) + })?; + Ok(output) + } + pub async fn run(&mut self) -> Result<()> { // Spawn input event listener tokio::task::spawn(events::poll_input_events(