Feat(command_interface): Add basic lua support

This commit is contained in:
Benedikt Peetz 2023-07-12 21:48:51 +02:00
parent ef5afcda02
commit 327450c64b
Signed by: bpeetz
GPG Key ID: A5E94010C3A642AD
9 changed files with 216 additions and 16 deletions

74
Cargo.lock generated
View File

@ -140,7 +140,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -151,7 +151,7 @@ checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -269,6 +269,15 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.13.0" version = "3.13.0"
@ -642,7 +651,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -841,7 +850,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -1278,6 +1287,15 @@ dependencies = [
"hashbrown 0.12.3", "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]] [[package]]
name = "maplit" name = "maplit"
version = "1.0.2" version = "1.0.2"
@ -1586,7 +1604,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -1701,7 +1719,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -2097,6 +2115,30 @@ dependencies = [
"winreg", "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]] [[package]]
name = "ruma" name = "ruma"
version = "0.7.4" version = "0.7.4"
@ -2286,7 +2328,7 @@ checksum = "27738cfea0d944ab72c3ed01f3d5f23ec4322af8a1431e40ce630e4c01ea74fd"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -2458,9 +2500,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.24" version = "2.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36ccaf716a23c35ff908f91c971a86a9a71af5998c1d8f10e828d9f55f68ac00" checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
@ -2498,7 +2540,7 @@ checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -2553,7 +2595,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -2632,7 +2674,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]
[[package]] [[package]]
@ -2652,7 +2694,9 @@ dependencies = [
"cli-log", "cli-log",
"crossterm", "crossterm",
"indexmap 2.0.0", "indexmap 2.0.0",
"lua_macros",
"matrix-sdk", "matrix-sdk",
"rlua",
"serde", "serde",
"tokio", "tokio",
"tokio-util", "tokio-util",
@ -2870,7 +2914,7 @@ dependencies = [
"once_cell", "once_cell",
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -2904,7 +2948,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
"wasm-bindgen-backend", "wasm-bindgen-backend",
"wasm-bindgen-shared", "wasm-bindgen-shared",
] ]
@ -3090,5 +3134,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [ dependencies = [
"proc-macro2 1.0.64", "proc-macro2 1.0.64",
"quote 1.0.29", "quote 1.0.29",
"syn 2.0.24", "syn 2.0.25",
] ]

View File

@ -7,6 +7,7 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
lua_macros = { path = "./lua_macros" }
tui = "0.19" tui = "0.19"
tui-textarea = { version = "0.2", features = ["crossterm"] } tui-textarea = { version = "0.2", features = ["crossterm"] }
crossterm = "0.25" crossterm = "0.25"
@ -17,3 +18,4 @@ tokio-util = "0.7"
serde = "1.0" serde = "1.0"
cli-log = "2.0" cli-log = "2.0"
indexmap = "2.0.0" indexmap = "2.0.0"
rlua = "0.19.7"

View File

@ -81,6 +81,7 @@
rust-stable rust-stable
rust-analyzer rust-analyzer
cargo-edit cargo-edit
cargo-expand
]; ];
inherit nativeBuildInputs buildInputs; inherit nativeBuildInputs buildInputs;
}; };

6
lua_macros/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
# build
/target
/result
# lua_macros is a library
Cargo.lock

12
lua_macros/Cargo.toml Normal file
View File

@ -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"

59
lua_macros/src/lib.rs Normal file
View File

@ -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()
}

View File

@ -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<String, rlua::Error> {
Ok(format!("Name is {}", name))
}

View File

@ -136,7 +136,42 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
}; };
} }
ui::MainInputPosition::CLI => { ui::MainInputPosition::CLI => {
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()));
}
};
};
} }
_ => (), _ => (),
}, },

View File

@ -26,10 +26,21 @@ pub struct App<'ui> {
channel_rx: mpsc::Receiver<event_types::Event>, channel_rx: mpsc::Receiver<event_types::Event>,
input_listener_killer: CancellationToken, input_listener_killer: CancellationToken,
matrix_listener_killer: CancellationToken, matrix_listener_killer: CancellationToken,
lua: Lua,
} }
impl App<'_> { impl App<'_> {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
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 path: &std::path::Path = Path::new("userdata/accounts.json");
let config = if path.exists() { let config = if path.exists() {
info!("Reading account config (userdata/accounts.json)"); info!("Reading account config (userdata/accounts.json)");
@ -49,9 +60,26 @@ impl App<'_> {
channel_rx, channel_rx,
input_listener_killer: CancellationToken::new(), input_listener_killer: CancellationToken::new(),
matrix_listener_killer: CancellationToken::new(), matrix_listener_killer: CancellationToken::new(),
lua: set_up_lua(),
}) })
} }
pub async fn handle_ci_event(&self, event: &str) -> Result<String> {
info!("Recieved ci event: `{event}`; executing..");
// TODO: Should the ci support more than strings?
let output = self.lua.context(|context| -> Result<String> {
let output = context
.load(&event)
.eval::<String>()
.with_context(|| format!("Failed to execute: `{event}`"))?;
info!("Function evaluated to: `{output}`");
Ok(output)
})?;
Ok(output)
}
pub async fn run(&mut self) -> Result<()> { pub async fn run(&mut self) -> Result<()> {
// Spawn input event listener // Spawn input event listener
tokio::task::spawn(events::poll_input_events( tokio::task::spawn(events::poll_input_events(