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"
|
||||
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]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.3"
|
||||
|
@ -1291,6 +1300,7 @@ dependencies = [
|
|||
name = "lua_macros"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2 1.0.64",
|
||||
"quote 1.0.29",
|
||||
"syn 2.0.25",
|
||||
|
|
12
flake.nix
12
flake.nix
|
@ -45,10 +45,14 @@
|
|||
overlays = [(import rust-overlay)];
|
||||
};
|
||||
|
||||
#rust-nightly = pkgs.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default);
|
||||
rust-stable = pkgs.rust-bin.stable.latest.default;
|
||||
nightly = true;
|
||||
|
||||
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; [
|
||||
pkg-config
|
||||
|
@ -78,7 +82,7 @@
|
|||
statix
|
||||
ltex-ls
|
||||
|
||||
rust-stable
|
||||
rust
|
||||
rust-analyzer
|
||||
cargo-edit
|
||||
cargo-expand
|
||||
|
|
|
@ -4,9 +4,10 @@ version = "0.1.0"
|
|||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate_type = ["proc-macro"]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
convert_case = "0.6.0"
|
||||
proc-macro2 = "1.0.64"
|
||||
quote = "1.0.29"
|
||||
syn = "2.0.25"
|
||||
syn = { version = "2.0.25", features = ["extra-traits", "full", "parsing"] }
|
||||
|
|
|
@ -1,59 +1,46 @@
|
|||
mod struct_to_ci_enum;
|
||||
mod mark_as_ci_command;
|
||||
|
||||
use mark_as_ci_command::generate_final_function;
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn;
|
||||
use quote::quote;
|
||||
use struct_to_ci_enum::{generate_command_enum, generate_generate_ci_function};
|
||||
use syn::{self, ItemFn};
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn generate_ci_functions(_: TokenStream, input: TokenStream) -> TokenStream {
|
||||
pub fn turn_struct_to_ci_commands(_attrs: 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)
|
||||
}
|
||||
let generate_ci_function: TokenStream2 = generate_generate_ci_function(&input);
|
||||
|
||||
let command_enum = generate_command_enum(&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
|
||||
)
|
||||
);
|
||||
#command_enum
|
||||
#generate_ci_function
|
||||
}
|
||||
.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
|
||||
/// Turn a function into a valid ci command function
|
||||
#[proc_macro_attribute]
|
||||
pub fn ci_command(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// Construct a representation of Rust code as a syntax tree
|
||||
// that we can manipulate
|
||||
let mut input: ItemFn = syn::parse(input)
|
||||
.expect("This should always be valid rust code, as it's extracted from direct code");
|
||||
|
||||
// Build the trait implementation
|
||||
let output_function: TokenStream2 = generate_final_function(&mut input);
|
||||
|
||||
//panic!("{:#?}", output_function);
|
||||
quote! {
|
||||
#output_function
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
.into()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use syn::{
|
||||
braced,
|
||||
punctuated::Punctuated,
|
||||
token::{Comma, Semi},
|
||||
Block, Expr, FnArg, Stmt, Token,
|
||||
};
|
||||
|
||||
pub fn generate_final_function(input: &mut syn::ItemFn) -> TokenStream2 {
|
||||
append_tx_send_code(input);
|
||||
|
||||
let output: TokenStream2 = syn::parse(input.into_token_stream().into())
|
||||
.expect("This is generated from valid rust code, it should stay that way.");
|
||||
|
||||
output
|
||||
}
|
||||
|
||||
fn append_tx_send_code(input: &mut syn::ItemFn) -> &mut syn::ItemFn {
|
||||
let function_name_pascal = format_ident!(
|
||||
"{}",
|
||||
input
|
||||
.sig
|
||||
.ident
|
||||
.clone()
|
||||
.to_string()
|
||||
.from_case(Case::Snake)
|
||||
.to_case(Case::Pascal)
|
||||
);
|
||||
|
||||
let tx_send = quote! {
|
||||
{
|
||||
let tx = context.named_registry_value("sender_for_ci_commands");
|
||||
|
||||
tx
|
||||
.send(Event::CommandEvent(Commands::#function_name_pascal))
|
||||
.expect("This should work, as the reciever is not dropped");
|
||||
}
|
||||
};
|
||||
|
||||
let content;
|
||||
braced!(content in tx_send);
|
||||
let mut tx_send_expr: Vec<Stmt> =
|
||||
Block::parse_within(content).expect("This is a static string, it will always parse");
|
||||
|
||||
let mut new_stmts: Vec<Stmt> = Vec::with_capacity(input.block.stmts.len() + tx_send_expr.len());
|
||||
new_stmts.append(&mut tx_send_expr);
|
||||
new_stmts.append(&mut input.block.stmts);
|
||||
input.block.stmts = new_stmts;
|
||||
input
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
use convert_case::{Case, Casing};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{self, ReturnType};
|
||||
|
||||
pub fn generate_generate_ci_function(input: &syn::DeriveInput) -> TokenStream2 {
|
||||
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 implemented for structs"),
|
||||
};
|
||||
|
||||
let gen = quote! {
|
||||
pub fn generate_ci_functions(
|
||||
context: &mut rlua::Context,
|
||||
tx: std::sync::mpsc::Sender<crate::app::events::event_types::Event>)
|
||||
{
|
||||
let globals = context.globals();
|
||||
#input_tokens
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
}
|
||||
|
||||
pub fn generate_command_enum(input: &syn::DeriveInput) -> TokenStream2 {
|
||||
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 enum_variant_type = match &field.ty {
|
||||
syn::Type::BareFn(function) => {
|
||||
let return_path: &ReturnType = &function.output;
|
||||
match return_path {
|
||||
ReturnType::Default => None,
|
||||
ReturnType::Type(_, return_type) => Some(match *(return_type.to_owned()) {
|
||||
syn::Type::Path(path_type) => path_type
|
||||
.path
|
||||
.get_ident()
|
||||
.expect("A path should either be complete, or only conain one segment")
|
||||
.to_owned(),
|
||||
_ => unimplemented!("This is only implemented for path types"),
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => unimplemented!("This is only implemented for bare function types"),
|
||||
};
|
||||
|
||||
let enum_variant_name = format_ident!(
|
||||
"{}",
|
||||
field_ident.to_string().from_case(Case::Snake).to_case(Case::Pascal)
|
||||
);
|
||||
if enum_variant_type.is_some() {
|
||||
quote! {
|
||||
#enum_variant_name (#enum_variant_type),
|
||||
}
|
||||
.into()
|
||||
} else {
|
||||
quote! {
|
||||
#enum_variant_name,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
_ => unimplemented!("Only implemented for named fileds"),
|
||||
},
|
||||
_ => unimplemented!("Only implemented for structs"),
|
||||
};
|
||||
|
||||
let gen = quote! {
|
||||
pub enum Commands {
|
||||
#input_tokens
|
||||
}
|
||||
};
|
||||
gen.into()
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
use anyhow::Result;
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use super::events::event_types::Event;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Command {
|
||||
// Closes the application
|
||||
Exit,
|
||||
|
||||
CommandLineShow,
|
||||
CommandLineHide,
|
||||
|
||||
CyclePlanes,
|
||||
CyclePlanesRev,
|
||||
|
||||
// sends a message to the current room
|
||||
RoomMessageSend(String),
|
||||
}
|
||||
|
||||
pub async fn execute(channel: &mpsc::Sender<Event>, command: Command) -> Result<()> {
|
||||
let event = Event::CommandEvent(command);
|
||||
channel.send(event).await?;
|
||||
Ok(())
|
||||
}
|
|
@ -1,13 +1,47 @@
|
|||
use lua_macros::generate_ci_functions;
|
||||
use lua_macros::{turn_struct_to_ci_commands, ci_command};
|
||||
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()]
|
||||
use super::events::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_commands]
|
||||
struct Commands<'lua> {
|
||||
greet: Function<'lua>,
|
||||
greet: fn(usize) -> String,
|
||||
|
||||
// Closes the application
|
||||
exit: fn(),
|
||||
//command_line_show: fn(),
|
||||
//command_line_hide: fn(),
|
||||
|
||||
//cycle_planes: fn(),
|
||||
//cycle_planes_rev: fn(),
|
||||
|
||||
//// sends a message to the current room
|
||||
//room_message_send: fn(String),
|
||||
}
|
||||
|
||||
fn greet(context: Context, name: String) -> Result<String, rlua::Error> {
|
||||
#[ci_command]
|
||||
fn greet(_context: Context, name: String) -> Result<String, rlua::Error> {
|
||||
Ok(format!("Name is {}", name))
|
||||
}
|
||||
|
||||
#[ci_command]
|
||||
fn exit(_context: Context, _input_str: String) -> Result<(), rlua::Error> {
|
||||
Event::CommandEvent(Commands::Exit);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use cli_log::warn;
|
||||
|
||||
use crate::app::{events::event_types::EventStatus, App};
|
||||
|
||||
pub async fn handle(app: &mut App<'_>, output: String) -> Result<EventStatus> {
|
||||
info!("Recieved command output: `{}`");
|
||||
if let Some(cli) = app.ui.cli {
|
||||
cli.
|
||||
} else {
|
||||
warn!("There is no way to display the output!");
|
||||
}
|
||||
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
|
@ -5,32 +5,30 @@ use cli_log::info;
|
|||
pub async fn handle(app: &mut App<'_>, command: &Command) -> Result<EventStatus> {
|
||||
info!("Handling command: {:#?}", command);
|
||||
|
||||
Ok(match command {
|
||||
Command::Exit => EventStatus::Terminate,
|
||||
match command {
|
||||
Command::Exit => return Ok(EventStatus::Terminate),
|
||||
|
||||
Command::CommandLineShow => {
|
||||
app.ui.cli_enable();
|
||||
EventStatus::Ok
|
||||
}
|
||||
Command::CommandLineHide => {
|
||||
app.ui.cli_disable();
|
||||
EventStatus::Ok
|
||||
}
|
||||
|
||||
Command::CyclePlanes => {
|
||||
app.ui.cycle_main_input_position();
|
||||
EventStatus::Ok
|
||||
}
|
||||
Command::CyclePlanesRev => {
|
||||
app.ui.cycle_main_input_position_rev();
|
||||
EventStatus::Ok
|
||||
}
|
||||
|
||||
Command::RoomMessageSend(msg) => {
|
||||
if let Some(room) = app.status.room_mut() {
|
||||
room.send(msg.clone()).await?;
|
||||
} else {
|
||||
// FIXME: This needs a callback to return an error.
|
||||
}
|
||||
EventStatus::Ok
|
||||
}
|
||||
})
|
||||
}
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
use anyhow::{Context, Result};
|
||||
use cli_log::info;
|
||||
|
||||
use crate::app::{App, events::event_types::{EventStatus, Event}};
|
||||
|
||||
pub async fn handle(app: &mut App<'_>, command: &str) -> Result<EventStatus> {
|
||||
info!("Recieved ci command: `{command}`; executing..");
|
||||
|
||||
// TODO: Should the ci support more than strings?
|
||||
let output = app.lua.context(|context| -> Result<String> {
|
||||
let output = context
|
||||
.load(&command)
|
||||
.eval::<String>()
|
||||
.with_context(|| format!("Failed to execute: `{command}`"))?;
|
||||
info!("Function evaluated to: `{output}`");
|
||||
Ok(output)
|
||||
})?;
|
||||
|
||||
app.transmitter.send(Event::CiOutput(output));
|
||||
|
||||
Ok(EventStatus::Ok)
|
||||
}
|
|
@ -2,7 +2,7 @@ use anyhow::Result;
|
|||
use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
use crate::{
|
||||
app::{command, command::Command, events::event_types::EventStatus, App},
|
||||
app::{events::event_types::EventStatus, App},
|
||||
ui::central,
|
||||
};
|
||||
|
||||
|
@ -11,7 +11,7 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
|
|||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Esc, ..
|
||||
}) => {
|
||||
command::execute(app.channel_tx(), Command::Exit).await?;
|
||||
app.transmitter.send(Event::CommandEvent(Command::Exit)).await?;
|
||||
}
|
||||
CrosstermEvent::Key(KeyEvent {
|
||||
code: KeyCode::Tab, ..
|
||||
|
@ -153,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"
|
||||
)
|
||||
.to_owned();
|
||||
let output = app.handle_ci_event(&cli_event).await?;
|
||||
let output = (&cli_event).await?;
|
||||
|
||||
// delete the old text:
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
pub mod command;
|
||||
pub mod lua_command;
|
||||
pub mod main;
|
||||
pub mod matrix;
|
||||
pub mod setup;
|
||||
pub mod ci_output;
|
||||
|
|
|
@ -5,7 +5,7 @@ use crossterm::event::Event as CrosstermEvent;
|
|||
|
||||
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;
|
||||
|
||||
|
@ -14,11 +14,21 @@ pub enum Event {
|
|||
InputEvent(CrosstermEvent),
|
||||
MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse),
|
||||
CommandEvent(Command),
|
||||
LuaCommand(String),
|
||||
CiOutput(String),
|
||||
}
|
||||
|
||||
impl Event {
|
||||
pub async fn handle(&self, app: &mut App<'_>) -> Result<EventStatus> {
|
||||
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)
|
||||
.await
|
||||
.with_context(|| format!("Failed to handle matrix event: `{:#?}`", event)),
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#[derive(Debug)]
|
||||
pub enum EventStatus {
|
||||
/// The event was handled successfully
|
||||
Ok,
|
||||
|
||||
/// TODO
|
||||
Finished,
|
||||
|
||||
/// Terminate the whole application
|
||||
Terminate,
|
||||
}
|
||||
|
|
|
@ -1,30 +1,32 @@
|
|||
pub mod command;
|
||||
pub mod command_interface;
|
||||
pub mod events;
|
||||
pub mod status;
|
||||
pub mod transmitter;
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use accounts::{Account, AccountsManager};
|
||||
use anyhow::{Context, Error, Result};
|
||||
use cli_log::info;
|
||||
use matrix_sdk::Client;
|
||||
use rlua::Lua;
|
||||
use status::{State, Status};
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use crate::{accounts, app::command_interface::generate_ci_functions, ui::{central, setup}};
|
||||
use crate::{
|
||||
accounts::{Account, AccountsManager},
|
||||
app::{command_interface::generate_ci_functions, events::event_types::Event},
|
||||
ui::{central, setup},
|
||||
};
|
||||
|
||||
use self::events::event_types::{self, Event};
|
||||
use self::{events::event_types, transmitter::Transmitter};
|
||||
|
||||
pub struct App<'ui> {
|
||||
ui: central::UI<'ui>,
|
||||
accounts_manager: accounts::AccountsManager,
|
||||
accounts_manager: AccountsManager,
|
||||
status: Status,
|
||||
|
||||
channel_tx: mpsc::Sender<event_types::Event>,
|
||||
channel_rx: mpsc::Receiver<event_types::Event>,
|
||||
transmitter: Transmitter,
|
||||
input_listener_killer: CancellationToken,
|
||||
matrix_listener_killer: CancellationToken,
|
||||
|
||||
|
@ -33,11 +35,11 @@ pub struct App<'ui> {
|
|||
|
||||
impl App<'_> {
|
||||
pub fn new() -> Result<Self> {
|
||||
fn set_up_lua() -> Lua {
|
||||
fn set_up_lua(tx: Sender<Event>) -> Lua {
|
||||
let lua = Lua::new();
|
||||
|
||||
lua.context(|mut lua_context| {
|
||||
generate_ci_functions(&mut lua_context);
|
||||
generate_ci_functions(&mut lua_context, tx);
|
||||
});
|
||||
lua
|
||||
}
|
||||
|
@ -50,41 +52,24 @@ impl App<'_> {
|
|||
None
|
||||
};
|
||||
|
||||
let (channel_tx, channel_rx) = mpsc::channel(256);
|
||||
|
||||
let transmitter = Transmitter::new();
|
||||
Ok(Self {
|
||||
ui: central::UI::new()?,
|
||||
accounts_manager: AccountsManager::new(config)?,
|
||||
status: Status::new(None),
|
||||
|
||||
channel_tx,
|
||||
channel_rx,
|
||||
transmitter,
|
||||
input_listener_killer: CancellationToken::new(),
|
||||
matrix_listener_killer: CancellationToken::new(),
|
||||
|
||||
lua: set_up_lua(),
|
||||
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<()> {
|
||||
// Spawn input event listener
|
||||
tokio::task::spawn(events::poll_input_events(
|
||||
self.channel_tx.clone(),
|
||||
self.transmitter.tx(),
|
||||
self.input_listener_killer.clone(),
|
||||
));
|
||||
|
||||
|
@ -100,15 +85,16 @@ impl App<'_> {
|
|||
self.status.set_state(State::Main);
|
||||
self.ui.update(&self.status).await?;
|
||||
|
||||
let event: event_types::Event = match self.channel_rx.recv().await {
|
||||
Some(e) => e,
|
||||
None => return Err(Error::msg("Event channel has no senders")),
|
||||
};
|
||||
let event = self
|
||||
.transmitter
|
||||
.recv()
|
||||
.await
|
||||
.context("Failed to get next event")?;
|
||||
|
||||
match event.handle(self).await? {
|
||||
event_types::EventStatus::Ok => (),
|
||||
event_types::EventStatus::Finished => (),
|
||||
event_types::EventStatus::Terminate => break,
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -123,10 +109,11 @@ impl App<'_> {
|
|||
self.status.set_state(State::Setup);
|
||||
self.ui.update_setup().await?;
|
||||
|
||||
let event: event_types::Event = match self.channel_rx.recv().await {
|
||||
Some(e) => e,
|
||||
None => return Err(Error::msg("Event channel has no senders")),
|
||||
};
|
||||
let event = self
|
||||
.transmitter
|
||||
.recv()
|
||||
.await
|
||||
.context("Failed to get next event")?;
|
||||
|
||||
match event.handle(self).await? {
|
||||
event_types::EventStatus::Ok => (),
|
||||
|
@ -150,7 +137,7 @@ impl App<'_> {
|
|||
|
||||
// Spawn Matrix Event Listener
|
||||
tokio::task::spawn(events::poll_matrix_events(
|
||||
self.channel_tx.clone(),
|
||||
self.transmitter.tx(),
|
||||
self.matrix_listener_killer.clone(),
|
||||
client.clone(),
|
||||
));
|
||||
|
@ -204,8 +191,4 @@ impl App<'_> {
|
|||
pub fn client(&self) -> Option<&Client> {
|
||||
self.accounts_manager.client()
|
||||
}
|
||||
|
||||
pub fn channel_tx(&self) -> &mpsc::Sender<Event> {
|
||||
&self.channel_tx
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use anyhow::{Error, Result};
|
||||
use cli_log::{warn, info};
|
||||
use cli_log::{info, warn};
|
||||
use indexmap::IndexMap;
|
||||
use matrix_sdk::{
|
||||
room::MessagesOptions,
|
||||
|
@ -62,7 +62,7 @@ impl Room {
|
|||
let events = self.matrix_room.messages(messages_options).await?;
|
||||
self.timeline_end = events.end;
|
||||
|
||||
for event in events.chunk.iter() {
|
||||
for event in events.chunk {
|
||||
self.timeline.insert(
|
||||
0,
|
||||
match event.event.deserialize() {
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
use anyhow::{bail, Context, Result};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use super::events::event_types::Event;
|
||||
|
||||
pub struct Transmitter {
|
||||
tx: mpsc::Sender<Event>,
|
||||
rx: mpsc::Receiver<Event>,
|
||||
}
|
||||
|
||||
impl Transmitter {
|
||||
pub fn new() -> Transmitter {
|
||||
let (tx, rx) = mpsc::channel(256);
|
||||
Transmitter { tx, rx }
|
||||
}
|
||||
pub fn tx(&self) -> mpsc::Sender<Event> {
|
||||
self.tx.to_owned()
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Result<Event> {
|
||||
match self.rx.recv().await {
|
||||
Some(event) => Ok(event),
|
||||
None => bail!("Event channel has no senders"),
|
||||
}
|
||||
}
|
||||
pub async fn send(&mut self, event: Event) -> Result<()> {
|
||||
self.tx.send(event).await.context("Failed to send event")
|
||||
}
|
||||
}
|
Reference in New Issue