Compare commits
1 Commits
866ec7c277
...
52b77aee4b
Author | SHA1 | Date |
---|---|---|
Benedikt Peetz | 52b77aee4b |
|
@ -415,6 +415,15 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
@ -1291,6 +1300,7 @@ dependencies = [
|
||||||
name = "lua_macros"
|
name = "lua_macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
"proc-macro2 1.0.64",
|
"proc-macro2 1.0.64",
|
||||||
"quote 1.0.29",
|
"quote 1.0.29",
|
||||||
"syn 2.0.25",
|
"syn 2.0.25",
|
||||||
|
|
12
flake.nix
12
flake.nix
|
@ -45,10 +45,14 @@
|
||||||
overlays = [(import rust-overlay)];
|
overlays = [(import rust-overlay)];
|
||||||
};
|
};
|
||||||
|
|
||||||
#rust-nightly = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default);
|
nightly = true;
|
||||||
rust-stable = pkgs.rust-bin.stable.latest.default;
|
|
||||||
|
|
||||||
craneLib = (crane.mkLib pkgs).overrideToolchain rust-stable;
|
rust =
|
||||||
|
if nightly
|
||||||
|
then pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default)
|
||||||
|
else pkgs.rust-bin.stable.latest.default;
|
||||||
|
|
||||||
|
craneLib = (crane.mkLib pkgs).overrideToolchain rust;
|
||||||
|
|
||||||
nativeBuildInputs = with pkgs; [
|
nativeBuildInputs = with pkgs; [
|
||||||
pkg-config
|
pkg-config
|
||||||
|
@ -78,7 +82,7 @@
|
||||||
statix
|
statix
|
||||||
ltex-ls
|
ltex-ls
|
||||||
|
|
||||||
rust-stable
|
rust
|
||||||
rust-analyzer
|
rust-analyzer
|
||||||
cargo-edit
|
cargo-edit
|
||||||
cargo-expand
|
cargo-expand
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
|
||||||
use quote::ToTokens;
|
|
||||||
|
|
||||||
// TODO: Do we need this noop?
|
|
||||||
pub fn generate_default_lua_function(input: &syn::Field) -> TokenStream2 {
|
|
||||||
let output: TokenStream2 = syn::parse(input.into_token_stream().into())
|
|
||||||
.expect("This is generated from valid rust code, it should stay that way.");
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
mod generate_noop_lua_function;
|
|
||||||
mod mark_as_ci_command;
|
|
||||||
mod struct_to_ci_enum;
|
mod struct_to_ci_enum;
|
||||||
|
mod mark_as_ci_command;
|
||||||
|
|
||||||
use generate_noop_lua_function::generate_default_lua_function;
|
|
||||||
use mark_as_ci_command::generate_final_function;
|
use mark_as_ci_command::generate_final_function;
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use struct_to_ci_enum::{generate_command_enum, generate_generate_ci_function};
|
use struct_to_ci_enum::{generate_command_enum, generate_generate_ci_function};
|
||||||
use syn::{self, ItemFn, Field, parse::Parser};
|
use syn::{self, ItemFn};
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn turn_struct_to_ci_commands(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn turn_struct_to_ci_commands(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
@ -29,26 +27,6 @@ pub fn turn_struct_to_ci_commands(_attrs: TokenStream, input: TokenStream) -> To
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a default lua function implementation.
|
|
||||||
#[proc_macro_attribute]
|
|
||||||
pub fn gen_lua_function(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
|
||||||
// Construct a representation of Rust code as a syntax tree
|
|
||||||
// that we can manipulate
|
|
||||||
//
|
|
||||||
let parser = Field::parse_named;
|
|
||||||
let input = parser.parse(input)
|
|
||||||
.expect("This is only defined for named fileds.");
|
|
||||||
|
|
||||||
|
|
||||||
// Build the trait implementation
|
|
||||||
let default_lua_function: TokenStream2 = generate_default_lua_function(&input);
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
#default_lua_function
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Turn a function into a valid ci command function
|
/// Turn a function into a valid ci command function
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn ci_command(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
pub fn ci_command(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||||
use quote::{format_ident, quote, ToTokens};
|
use quote::{format_ident, quote, ToTokens};
|
||||||
use syn::{Block, Expr, ExprBlock, GenericArgument, ReturnType, Stmt, Type};
|
use syn::{
|
||||||
|
braced,
|
||||||
|
punctuated::Punctuated,
|
||||||
|
token::{Comma, Semi},
|
||||||
|
Block, Expr, FnArg, Stmt, Token,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn generate_final_function(input: &mut syn::ItemFn) -> TokenStream2 {
|
pub fn generate_final_function(input: &mut syn::ItemFn) -> TokenStream2 {
|
||||||
append_tx_send_code(input);
|
append_tx_send_code(input);
|
||||||
|
@ -24,137 +29,23 @@ fn append_tx_send_code(input: &mut syn::ItemFn) -> &mut syn::ItemFn {
|
||||||
.to_case(Case::Pascal)
|
.to_case(Case::Pascal)
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx_send = match &input.sig.output {
|
let tx_send = quote! {
|
||||||
syn::ReturnType::Default => {
|
{
|
||||||
todo!(
|
let tx = context.named_registry_value("sender_for_ci_commands");
|
||||||
"Does this case even trigger? All functions should have a output of (Result<$type, rlua::Error>)"
|
|
||||||
);
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
let tx: std::sync::mpsc::Sender<crate::app::events::event_types::Event> =
|
|
||||||
context
|
|
||||||
.named_registry_value("sender_for_ci_commands")
|
|
||||||
.expect("This exists, it was set before");
|
|
||||||
|
|
||||||
tx
|
tx
|
||||||
.send(Event::CommandEvent(Command::#function_name_pascal))
|
.send(Event::CommandEvent(Commands::#function_name_pascal))
|
||||||
.expect("This should work, as the reciever is not dropped");
|
.expect("This should work, as the reciever is not dropped");
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
syn::ReturnType::Type(_, ret_type) => {
|
|
||||||
let return_type = match *(ret_type.clone()) {
|
|
||||||
syn::Type::Path(path) => {
|
|
||||||
match path
|
|
||||||
.path
|
|
||||||
.segments
|
|
||||||
.first()
|
|
||||||
.expect("This is expected to be only one path segment")
|
|
||||||
.arguments
|
|
||||||
.to_owned()
|
|
||||||
{
|
|
||||||
syn::PathArguments::AngleBracketed(angled_path) => {
|
|
||||||
let angled_path = angled_path.args.to_owned();
|
|
||||||
let filtered_paths: Vec<_> = angled_path
|
|
||||||
.into_iter()
|
|
||||||
.filter(|generic_arg| {
|
|
||||||
if let GenericArgument::Type(generic_type) = generic_arg {
|
|
||||||
if let Type::Path(_) = generic_type {
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// There should only be two segments (the type is <String, rlua::Error>)
|
|
||||||
if filtered_paths.len() > 2 {
|
|
||||||
unreachable!(
|
|
||||||
"There should be no more than two filtered_output, but got: {:#?}",
|
|
||||||
filtered_paths
|
|
||||||
)
|
|
||||||
} else if filtered_paths.len() <= 0 {
|
|
||||||
unreachable!(
|
|
||||||
"There should be more than zero filtered_output, but got: {:#?}",
|
|
||||||
filtered_paths
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if filtered_paths.len() == 2 {
|
|
||||||
// There is something else than rlua
|
|
||||||
let gen_type = if let GenericArgument::Type(ret_type) =
|
|
||||||
filtered_paths
|
|
||||||
.first()
|
|
||||||
.expect("One path segment should exists")
|
|
||||||
.to_owned()
|
|
||||||
{
|
|
||||||
ret_type
|
|
||||||
} else {
|
|
||||||
unreachable!("These were filtered above.");
|
|
||||||
};
|
|
||||||
let return_type_as_type_prepared = quote! {-> #gen_type};
|
|
||||||
|
|
||||||
let return_type_as_return_type: ReturnType = syn::parse(
|
|
||||||
return_type_as_type_prepared.to_token_stream().into(),
|
|
||||||
)
|
|
||||||
.expect("This is valid.");
|
|
||||||
return_type_as_return_type
|
|
||||||
} else {
|
|
||||||
// There is only rlua
|
|
||||||
ReturnType::Default
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unimplemented!("Only for angled paths"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => unimplemented!("Only for path types"),
|
|
||||||
};
|
|
||||||
match return_type {
|
|
||||||
ReturnType::Default => {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
let tx: std::sync::mpsc::Sender<crate::app::events::event_types::Event> =
|
|
||||||
context
|
|
||||||
.named_registry_value("sender_for_ci_commands")
|
|
||||||
.expect("This exists, it was set before");
|
|
||||||
|
|
||||||
tx
|
|
||||||
.send(Event::CommandEvent(Command::#function_name_pascal))
|
|
||||||
.expect("This should work, as the reciever is not dropped");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ReturnType::Type(_, _) => {
|
|
||||||
quote! {
|
|
||||||
{
|
|
||||||
let tx: std::sync::mpsc::Sender<crate::app::events::event_types::Event> =
|
|
||||||
context
|
|
||||||
.named_registry_value("sender_for_ci_commands")
|
|
||||||
.expect("This exists, it was set before");
|
|
||||||
|
|
||||||
tx
|
|
||||||
.send(Event::CommandEvent(Command::#function_name_pascal(input_str)))
|
|
||||||
.expect("This should work, as the reciever is not dropped");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tx_send_block: Block =
|
let content;
|
||||||
syn::parse(tx_send.into()).expect("This is a static string, it will always parse");
|
braced!(content in tx_send);
|
||||||
let tx_send_expr_block = ExprBlock {
|
let mut tx_send_expr: Vec<Stmt> =
|
||||||
attrs: vec![],
|
Block::parse_within(content).expect("This is a static string, it will always parse");
|
||||||
label: None,
|
|
||||||
block: tx_send_block,
|
|
||||||
};
|
|
||||||
let mut tx_send_stmt = vec![Stmt::Expr(Expr::Block(tx_send_expr_block), None)];
|
|
||||||
|
|
||||||
let mut new_stmts: Vec<Stmt> = Vec::with_capacity(input.block.stmts.len() + 1);
|
let mut new_stmts: Vec<Stmt> = Vec::with_capacity(input.block.stmts.len() + tx_send_expr.len());
|
||||||
new_stmts.append(&mut tx_send_stmt);
|
new_stmts.append(&mut tx_send_expr);
|
||||||
new_stmts.append(&mut input.block.stmts);
|
new_stmts.append(&mut input.block.stmts);
|
||||||
input.block.stmts = new_stmts;
|
input.block.stmts = new_stmts;
|
||||||
input
|
input
|
||||||
|
|
|
@ -4,81 +4,53 @@ use quote::{format_ident, quote};
|
||||||
use syn::{self, ReturnType};
|
use syn::{self, ReturnType};
|
||||||
|
|
||||||
pub fn generate_generate_ci_function(input: &syn::DeriveInput) -> TokenStream2 {
|
pub fn generate_generate_ci_function(input: &syn::DeriveInput) -> TokenStream2 {
|
||||||
let mut functions_to_generate: Vec<TokenStream2> = vec![];
|
|
||||||
let input_tokens: TokenStream2 = match &input.data {
|
let input_tokens: TokenStream2 = match &input.data {
|
||||||
syn::Data::Struct(input) => match &input.fields {
|
syn::Data::Struct(input) => match &input.fields {
|
||||||
syn::Fields::Named(named_fields) => named_fields
|
syn::Fields::Named(named_fields) => named_fields
|
||||||
.named
|
.named
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| -> TokenStream2 {
|
.map(|field| -> TokenStream2 {
|
||||||
if field.attrs.iter().any(|attribute| {
|
let field_ident = field.ident.as_ref().expect(
|
||||||
attribute.path()
|
"These are only the named field, thus they all should have a name.",
|
||||||
== &syn::parse_str::<syn::Path>("gen_default_lua_function")
|
);
|
||||||
.expect("This is valid rust code")
|
let function_name_ident = format_ident!("fun_{}", field_ident);
|
||||||
}) {
|
let function_name = format!("{}", field_ident);
|
||||||
let function_name = field
|
quote! {
|
||||||
.ident
|
let #function_name_ident = context.create_function(#field_ident).expect(
|
||||||
.as_ref()
|
&format!(
|
||||||
.expect("These are only the named field, thus they all should have a name.");
|
"The function: `{}` should be defined",
|
||||||
functions_to_generate.push(quote! {
|
#function_name
|
||||||
#[ci_command]
|
)
|
||||||
fn #function_name(context: Context, input_str: String) -> Result<(), rlua::Error> {
|
);
|
||||||
Ok(())
|
|
||||||
}
|
globals.set(#function_name, #function_name_ident).expect(
|
||||||
});
|
&format!(
|
||||||
generate_ci_part(field)
|
"Setting a static global value ({}, fun_{}) should work",
|
||||||
} else {
|
#function_name,
|
||||||
generate_ci_part(field)
|
#function_name
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
||||||
_ => unimplemented!("Only implemented for named fileds"),
|
_ => unimplemented!("Only implemented for named fileds"),
|
||||||
},
|
},
|
||||||
_ => unimplemented!("Only implemented for structs"),
|
_ => unimplemented!("Only implemented for structs"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let functions_to_generate: TokenStream2 = functions_to_generate.into_iter().collect();
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
pub fn generate_ci_functions(
|
pub fn generate_ci_functions(
|
||||||
context: &mut rlua::Context,
|
context: &mut rlua::Context,
|
||||||
tx: std::sync::mpsc::Sender<crate::app::events::event_types::Event>)
|
tx: std::sync::mpsc::Sender<crate::app::events::event_types::Event>)
|
||||||
{
|
{
|
||||||
context.set_named_registry_value("sender_for_ci_commands", tx).expect("This should always work, as the value is added before all else");
|
|
||||||
let globals = context.globals();
|
let globals = context.globals();
|
||||||
#input_tokens
|
#input_tokens
|
||||||
}
|
}
|
||||||
#functions_to_generate
|
|
||||||
};
|
};
|
||||||
gen.into()
|
gen.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_ci_part(field: &syn::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()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_command_enum(input: &syn::DeriveInput) -> TokenStream2 {
|
pub fn generate_command_enum(input: &syn::DeriveInput) -> TokenStream2 {
|
||||||
let input_tokens: TokenStream2 = match &input.data {
|
let input_tokens: TokenStream2 = match &input.data {
|
||||||
syn::Data::Struct(input) => match &input.fields {
|
syn::Data::Struct(input) => match &input.fields {
|
||||||
|
@ -131,8 +103,7 @@ pub fn generate_command_enum(input: &syn::DeriveInput) -> TokenStream2 {
|
||||||
};
|
};
|
||||||
|
|
||||||
let gen = quote! {
|
let gen = quote! {
|
||||||
#[derive(Debug)]
|
pub enum Commands {
|
||||||
pub enum Command {
|
|
||||||
#input_tokens
|
#input_tokens
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// FIXME: This file needs documentation with examples of how the proc macros work.
|
use lua_macros::{turn_struct_to_ci_commands, ci_command};
|
||||||
use lua_macros::{ci_command, turn_struct_to_ci_commands};
|
|
||||||
use rlua::Context;
|
use rlua::Context;
|
||||||
|
|
||||||
use super::events::event_types::Event;
|
use super::events::event_types::Event;
|
||||||
|
@ -25,28 +24,24 @@ struct Commands<'lua> {
|
||||||
greet: fn(usize) -> String,
|
greet: fn(usize) -> String,
|
||||||
|
|
||||||
// Closes the application
|
// Closes the application
|
||||||
#[gen_default_lua_function]
|
|
||||||
exit: fn(),
|
exit: fn(),
|
||||||
#[gen_default_lua_function]
|
//command_line_show: fn(),
|
||||||
command_line_show: fn(),
|
//command_line_hide: fn(),
|
||||||
#[gen_default_lua_function]
|
|
||||||
command_line_hide: fn(),
|
|
||||||
|
|
||||||
#[gen_default_lua_function]
|
//cycle_planes: fn(),
|
||||||
cycle_planes: fn(),
|
//cycle_planes_rev: fn(),
|
||||||
#[gen_default_lua_function]
|
|
||||||
cycle_planes_rev: fn(),
|
|
||||||
|
|
||||||
//// sends a message to the current room
|
//// sends a message to the current room
|
||||||
room_message_send: fn(String) -> String,
|
//room_message_send: fn(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ci_command]
|
#[ci_command]
|
||||||
fn greet(context: Context, input_str: String) -> Result<String, rlua::Error> {
|
fn greet(_context: Context, name: String) -> Result<String, rlua::Error> {
|
||||||
Ok(format!("Name is {}", input_str))
|
Ok(format!("Name is {}", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ci_command]
|
#[ci_command]
|
||||||
fn room_message_send(context: Context, input_str: String) -> Result<String, rlua::Error> {
|
fn exit(_context: Context, _input_str: String) -> Result<(), rlua::Error> {
|
||||||
Ok(format!("Sent message: {}", input_str))
|
Event::CommandEvent(Commands::Exit);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use anyhow::Result;
|
use anyhow::{bail, Context, Result};
|
||||||
use cli_log::info;
|
use cli_log::warn;
|
||||||
|
|
||||||
use crate::app::{events::event_types::EventStatus, App};
|
use crate::app::{events::event_types::EventStatus, App};
|
||||||
|
|
||||||
pub async fn handle(app: &mut App<'_>, output: &String) -> Result<EventStatus> {
|
pub async fn handle(app: &mut App<'_>, output: String) -> Result<EventStatus> {
|
||||||
info!("Recieved command output: `{}`", output);
|
info!("Recieved command output: `{}`");
|
||||||
app.ui.set_command_output(output);
|
if let Some(cli) = app.ui.cli {
|
||||||
|
cli.
|
||||||
|
} else {
|
||||||
|
warn!("There is no way to display the output!");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(EventStatus::Ok)
|
Ok(EventStatus::Ok)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,36 +1,34 @@
|
||||||
use crate::app::{events::event_types::EventStatus, App, command_interface::Command};
|
use crate::app::{command::Command, events::event_types::EventStatus, App};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use cli_log::info;
|
use cli_log::info;
|
||||||
|
|
||||||
pub async fn handle(app: &mut App<'_>, command: &Command) -> Result<EventStatus> {
|
pub async fn handle(app: &mut App<'_>, command: &Command) -> Result<EventStatus> {
|
||||||
info!("Handling command: {:#?}", command);
|
info!("Handling command: {:#?}", command);
|
||||||
|
|
||||||
Ok(match command {
|
match command {
|
||||||
Command::Exit => EventStatus::Terminate,
|
Command::Exit => return Ok(EventStatus::Terminate),
|
||||||
|
|
||||||
Command::CommandLineShow => {
|
Command::CommandLineShow => {
|
||||||
app.ui.cli_enable();
|
app.ui.cli_enable();
|
||||||
EventStatus::Ok
|
|
||||||
}
|
}
|
||||||
Command::CommandLineHide => {
|
Command::CommandLineHide => {
|
||||||
app.ui.cli_disable();
|
app.ui.cli_disable();
|
||||||
EventStatus::Ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::CyclePlanes => {
|
Command::CyclePlanes => {
|
||||||
app.ui.cycle_main_input_position();
|
app.ui.cycle_main_input_position();
|
||||||
EventStatus::Ok
|
|
||||||
}
|
}
|
||||||
Command::CyclePlanesRev => {
|
Command::CyclePlanesRev => {
|
||||||
app.ui.cycle_main_input_position_rev();
|
app.ui.cycle_main_input_position_rev();
|
||||||
EventStatus::Ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::RoomMessageSend(msg) => {
|
Command::RoomMessageSend(msg) => {
|
||||||
if let Some(room) = app.status.room_mut() {
|
if let Some(room) = app.status.room_mut() {
|
||||||
room.send(msg.clone()).await?;
|
room.send(msg.clone()).await?;
|
||||||
|
} else {
|
||||||
|
// FIXME: This needs a callback to return an error.
|
||||||
}
|
}
|
||||||
EventStatus::Ok
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
Ok(EventStatus::Ok)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,7 @@ use anyhow::Result;
|
||||||
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers};
|
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{events::event_types::EventStatus, App},
|
||||||
command_interface::Command,
|
|
||||||
events::event_types::{Event, EventStatus},
|
|
||||||
App,
|
|
||||||
},
|
|
||||||
ui::central,
|
ui::central,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,33 +11,25 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
code: KeyCode::Esc, ..
|
code: KeyCode::Esc, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.transmitter
|
app.transmitter.send(Event::CommandEvent(Command::Exit)).await?;
|
||||||
.send(Event::CommandEvent(Command::Exit))
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
code: KeyCode::Tab, ..
|
code: KeyCode::Tab, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.transmitter
|
command::execute(app.channel_tx(), Command::CyclePlanes).await?;
|
||||||
.send(Event::CommandEvent(Command::CyclePlanes))
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
code: KeyCode::BackTab,
|
code: KeyCode::BackTab,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.transmitter
|
command::execute(app.channel_tx(), Command::CyclePlanesRev).await?;
|
||||||
.send(Event::CommandEvent(Command::CyclePlanesRev))
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
code: KeyCode::Char('c'),
|
code: KeyCode::Char('c'),
|
||||||
modifiers: KeyModifiers::CONTROL,
|
modifiers: KeyModifiers::CONTROL,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.transmitter
|
command::execute(app.channel_tx(), Command::CommandLineShow).await?;
|
||||||
.send(Event::CommandEvent(Command::CommandLineShow))
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
input => match app.ui.input_position() {
|
input => match app.ui.input_position() {
|
||||||
central::InputPosition::MessageCompose => {
|
central::InputPosition::MessageCompose => {
|
||||||
|
@ -51,11 +39,11 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
|
||||||
modifiers: KeyModifiers::ALT,
|
modifiers: KeyModifiers::ALT,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.transmitter
|
command::execute(
|
||||||
.send(Event::CommandEvent(Command::RoomMessageSend(
|
app.channel_tx(),
|
||||||
app.ui.message_compose.lines().join("\n"),
|
Command::RoomMessageSend(app.ui.message_compose.lines().join("\n")),
|
||||||
)))
|
)
|
||||||
.await?;
|
.await?;
|
||||||
app.ui.message_compose_clear();
|
app.ui.message_compose_clear();
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -165,7 +153,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
|
||||||
"There can only be one line in the buffer, as we collect it on enter being inputted"
|
"There can only be one line in the buffer, as we collect it on enter being inputted"
|
||||||
)
|
)
|
||||||
.to_owned();
|
.to_owned();
|
||||||
let output = app.handle_ci_event(&cli_event).await?;
|
let output = (&cli_event).await?;
|
||||||
|
|
||||||
// delete the old text:
|
// delete the old text:
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
pub mod command;
|
pub mod command;
|
||||||
|
pub mod lua_command;
|
||||||
pub mod main;
|
pub mod main;
|
||||||
pub mod matrix;
|
pub mod matrix;
|
||||||
pub mod setup;
|
pub mod setup;
|
||||||
|
pub mod ci_output;
|
||||||
|
|
|
@ -3,9 +3,9 @@ mod handlers;
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use crossterm::event::Event as CrosstermEvent;
|
use crossterm::event::Event as CrosstermEvent;
|
||||||
|
|
||||||
use crate::app::{status::State, App, command_interface::Command};
|
use crate::app::{command::Command, status::State, App};
|
||||||
|
|
||||||
use self::handlers::{command, main, matrix, setup};
|
use self::handlers::{command, lua_command, main, matrix, setup};
|
||||||
|
|
||||||
use super::EventStatus;
|
use super::EventStatus;
|
||||||
|
|
||||||
|
@ -14,11 +14,21 @@ pub enum Event {
|
||||||
InputEvent(CrosstermEvent),
|
InputEvent(CrosstermEvent),
|
||||||
MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse),
|
MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse),
|
||||||
CommandEvent(Command),
|
CommandEvent(Command),
|
||||||
|
LuaCommand(String),
|
||||||
|
CiOutput(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Event {
|
impl Event {
|
||||||
pub async fn handle(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
pub async fn handle(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||||
match &self {
|
match &self {
|
||||||
|
Event::LuaCommand(command) => lua_command::handle(app, command)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("Failed to handle lua command: `{:#?}`", command)),
|
||||||
|
|
||||||
|
Event::CiOutput(output) => ci_output::handle(app, output)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("Failed to handle ci output: `{:#?}`", output)),
|
||||||
|
|
||||||
Event::MatrixEvent(event) => matrix::handle(app, event)
|
Event::MatrixEvent(event) => matrix::handle(app, event)
|
||||||
.await
|
.await
|
||||||
.with_context(|| format!("Failed to handle matrix event: `{:#?}`", event)),
|
.with_context(|| format!("Failed to handle matrix event: `{:#?}`", event)),
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum EventStatus {
|
pub enum EventStatus {
|
||||||
|
/// The event was handled successfully
|
||||||
Ok,
|
Ok,
|
||||||
|
|
||||||
|
/// TODO
|
||||||
Finished,
|
Finished,
|
||||||
|
|
||||||
|
/// Terminate the whole application
|
||||||
Terminate,
|
Terminate,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,13 +3,14 @@ pub mod events;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
pub mod transmitter;
|
pub mod transmitter;
|
||||||
|
|
||||||
use std::{path::Path, sync::mpsc::Sender as StdSender};
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{Context, Error, Result};
|
use anyhow::{Context, Error, Result};
|
||||||
use cli_log::info;
|
use cli_log::info;
|
||||||
use matrix_sdk::Client;
|
use matrix_sdk::Client;
|
||||||
use rlua::Lua;
|
use rlua::Lua;
|
||||||
use status::{State, Status};
|
use status::{State, Status};
|
||||||
|
use tokio::sync::mpsc::Sender;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -34,7 +35,7 @@ pub struct App<'ui> {
|
||||||
|
|
||||||
impl App<'_> {
|
impl App<'_> {
|
||||||
pub fn new() -> Result<Self> {
|
pub fn new() -> Result<Self> {
|
||||||
fn set_up_lua(tx: StdSender<Event>) -> Lua {
|
fn set_up_lua(tx: Sender<Event>) -> Lua {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
lua.context(|mut lua_context| {
|
lua.context(|mut lua_context| {
|
||||||
|
@ -61,25 +62,10 @@ impl App<'_> {
|
||||||
input_listener_killer: CancellationToken::new(),
|
input_listener_killer: CancellationToken::new(),
|
||||||
matrix_listener_killer: CancellationToken::new(),
|
matrix_listener_killer: CancellationToken::new(),
|
||||||
|
|
||||||
lua: set_up_lua(transmitter.std_tx()),
|
lua: set_up_lua(transmitter.tx()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
||||||
|
@ -107,8 +93,8 @@ impl App<'_> {
|
||||||
|
|
||||||
match event.handle(self).await? {
|
match event.handle(self).await? {
|
||||||
event_types::EventStatus::Ok => (),
|
event_types::EventStatus::Ok => (),
|
||||||
|
event_types::EventStatus::Finished => (),
|
||||||
event_types::EventStatus::Terminate => break,
|
event_types::EventStatus::Terminate => break,
|
||||||
_ => (),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use anyhow::{Error, Result};
|
use anyhow::{Error, Result};
|
||||||
use cli_log::{warn, info};
|
use cli_log::{info, warn};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use matrix_sdk::{
|
use matrix_sdk::{
|
||||||
room::MessagesOptions,
|
room::MessagesOptions,
|
||||||
|
@ -62,7 +62,7 @@ impl Room {
|
||||||
let events = self.matrix_room.messages(messages_options).await?;
|
let events = self.matrix_room.messages(messages_options).await?;
|
||||||
self.timeline_end = events.end;
|
self.timeline_end = events.end;
|
||||||
|
|
||||||
for event in events.chunk.iter() {
|
for event in events.chunk {
|
||||||
self.timeline.insert(
|
self.timeline.insert(
|
||||||
0,
|
0,
|
||||||
match event.event.deserialize() {
|
match event.event.deserialize() {
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::sync::mpsc as StdMpsc;
|
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::{bail, Context, Result};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
@ -8,29 +6,17 @@ use super::events::event_types::Event;
|
||||||
pub struct Transmitter {
|
pub struct Transmitter {
|
||||||
tx: mpsc::Sender<Event>,
|
tx: mpsc::Sender<Event>,
|
||||||
rx: mpsc::Receiver<Event>,
|
rx: mpsc::Receiver<Event>,
|
||||||
std_tx: StdMpsc::Sender<Event>,
|
|
||||||
std_rx: StdMpsc::Receiver<Event>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transmitter {
|
impl Transmitter {
|
||||||
pub fn new() -> Transmitter {
|
pub fn new() -> Transmitter {
|
||||||
let (std_tx, std_rx) = StdMpsc::channel();
|
|
||||||
let (tx, rx) = mpsc::channel(256);
|
let (tx, rx) = mpsc::channel(256);
|
||||||
Transmitter {
|
Transmitter { tx, rx }
|
||||||
tx,
|
|
||||||
rx,
|
|
||||||
std_tx,
|
|
||||||
std_rx,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn tx(&self) -> mpsc::Sender<Event> {
|
pub fn tx(&self) -> mpsc::Sender<Event> {
|
||||||
self.tx.to_owned()
|
self.tx.to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn std_tx(&self) -> StdMpsc::Sender<Event> {
|
|
||||||
self.std_tx.to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn recv(&mut self) -> Result<Event> {
|
pub async fn recv(&mut self) -> Result<Event> {
|
||||||
match self.rx.recv().await {
|
match self.rx.recv().await {
|
||||||
Some(event) => Ok(event),
|
Some(event) => Ok(event),
|
||||||
|
|
Reference in New Issue