Compare commits

...

2 Commits

Author SHA1 Message Date
Benedikt Peetz d88cf810a4
Fix(app::command_interface): Provide pre-generated file, to check syntax 2023-07-23 16:39:03 +02:00
Benedikt Peetz e792334d21
Feat(treewide): Add a feature based layout and repl subcommand
Compiling the whole tui stack, just to debug the lua command line seems
counterproductive to me. This allows to just compile the needed parts
for a basic lua repl.

As of yet the repl is just a mock-up, as the event handling can, as of
right now, not easily be separated from the tui.

To activate specific features add specify the on the cargo command line
like this:
```
cargo run --features "cli tui"
```
or add them to the `default` feature set in the `Cargo.toml`.
2023-07-23 16:39:01 +02:00
30 changed files with 389 additions and 73 deletions

205
Cargo.lock generated
View File

@ -73,6 +73,55 @@ dependencies = [
"libc",
]
[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is-terminal",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd"
[[package]]
name = "anstyle-parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.72"
@ -145,9 +194,9 @@ dependencies = [
[[package]]
name = "async-trait"
version = "0.1.71"
version = "0.1.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf"
checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09"
dependencies = [
"proc-macro2 1.0.66",
"quote 1.0.31",
@ -228,6 +277,12 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "blake3"
version = "1.4.1"
@ -382,6 +437,47 @@ dependencies = [
"inout",
]
[[package]]
name = "clap"
version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
dependencies = [
"clap_builder",
"clap_derive",
"once_cell",
]
[[package]]
name = "clap_builder"
version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050"
dependencies = [
"heck",
"proc-macro2 1.0.66",
"quote 1.0.31",
"syn 2.0.26",
]
[[package]]
name = "clap_lex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "cli-log"
version = "2.0.0"
@ -400,9 +496,15 @@ version = "0.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "const-oid"
version = "0.7.1"
@ -486,7 +588,7 @@ version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"crossterm_winapi",
"libc",
"mio",
@ -690,9 +792,9 @@ dependencies = [
[[package]]
name = "either"
version = "1.8.1"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "encoding_rs"
@ -738,12 +840,9 @@ checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "fastrand"
version = "1.9.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be"
dependencies = [
"instant",
]
checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
[[package]]
name = "file-size"
@ -1003,6 +1102,12 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.2"
@ -1194,23 +1299,23 @@ dependencies = [
"web-sys",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "ipnet"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]]
name = "is-terminal"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
"hermit-abi",
"rustix",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.5"
@ -1267,9 +1372,9 @@ checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0"
[[package]]
name = "lock_api"
@ -1614,7 +1719,7 @@ version = "0.10.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"cfg-if",
"foreign-types",
"libc",
@ -2064,7 +2169,7 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
@ -2073,7 +2178,7 @@ version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags",
"bitflags 1.3.2",
]
[[package]]
@ -2199,7 +2304,7 @@ dependencies = [
"thiserror",
"tracing",
"url",
"uuid 1.4.0",
"uuid 1.4.1",
"wildmatch",
]
@ -2255,13 +2360,12 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustix"
version = "0.37.23"
version = "0.38.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06"
checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5"
dependencies = [
"bitflags",
"bitflags 2.3.3",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
@ -2290,11 +2394,11 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "security-framework"
version = "2.9.1"
version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"core-foundation",
"core-foundation-sys",
"libc",
@ -2303,9 +2407,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
version = "2.9.0"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
dependencies = [
"core-foundation-sys",
"libc",
@ -2313,9 +2417,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.171"
version = "1.0.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
checksum = "3b88756493a5bd5e5395d53baa70b194b05764ab85b59e43e4b8f4e1192fa9b1"
dependencies = [
"serde_derive",
]
@ -2331,9 +2435,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.171"
version = "1.0.174"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
checksum = "6e5c3a298c7f978e53536f95a63bdc4c4a64550582f31a0359a9afda6aede62e"
dependencies = [
"proc-macro2 1.0.66",
"quote 1.0.31",
@ -2520,11 +2624,10 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.6.0"
version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998"
dependencies = [
"autocfg 1.1.0",
"cfg-if",
"fastrand",
"redox_syscall 0.3.5",
@ -2700,12 +2803,14 @@ name = "trinitrix"
version = "0.1.0"
dependencies = [
"anyhow",
"clap",
"cli-log",
"crossterm",
"indexmap 2.0.0",
"lua_macros",
"matrix-sdk",
"mlua",
"once_cell",
"serde",
"tokio",
"tokio-util",
@ -2725,7 +2830,7 @@ version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
dependencies = [
"bitflags",
"bitflags 1.3.2",
"cassowary",
"crossterm",
"unicode-segmentation",
@ -2808,6 +2913,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "0.7.4"
@ -2828,9 +2939,9 @@ dependencies = [
[[package]]
name = "uuid"
version = "1.4.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
dependencies = [
"getrandom 0.2.10",
"wasm-bindgen",

View File

@ -6,16 +6,29 @@ license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["cli"]
full = ["cli", "tui"]
cli = ["tokio/io-std"]
tui = ["dep:tui", "dep:tui-textarea", "dep:crossterm", "dep:tokio-util", "dep:serde", "dep:indexmap"]
[dependencies]
lua_macros = { path = "./lua_macros" }
tui = "0.19"
tui-textarea = { version = "0.2", features = ["crossterm"] }
crossterm = "0.25"
matrix-sdk = "0.6"
anyhow = "1.0"
tokio = { version = "1.29", features = ["macros", "rt-multi-thread"] }
tokio-util = "0.7"
serde = "1.0"
clap = { version = "4.3.19", features = ["derive"] }
cli-log = "2.0"
indexmap = "2.0.0"
anyhow = "1.0"
matrix-sdk = "0.6"
tokio = { version = "1.29", features = ["macros", "rt-multi-thread"] }
# lua stuff
lua_macros = { path = "./lua_macros" }
mlua = { version = "0.8.9", features = ["lua54", "async", "send"] }
once_cell = "1.18.0"
# tui feature specific parts
tui = {version = "0.19", optional = true}
tui-textarea = { version = "0.2", features = ["crossterm"], optional = true }
crossterm = { version = "0.25", optional = true }
tokio-util = { version = "0.7", optional = true }
serde = { version = "1.0", optional = true }
indexmap = { version = "2.0.0", optional = true }

23
src/cli.rs Normal file
View File

@ -0,0 +1,23 @@
use clap::{Parser, Subcommand};
// TODO: The description could be better
/// A terminal client for the matrix chat protocol
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
pub struct Args {
#[command(subcommand)]
/// The subcommand to execute, default is start
pub subcommand: Option<Command>,
}
#[derive(Subcommand, Debug)]
pub enum Command {
/// Starts a repl, for the lua interface
#[cfg(feature = "cli")]
#[clap(value_parser)]
Repl {},
/// Starts the main tui client
#[cfg(feature = "tui")]
#[clap(value_parser)]
Start {},
}

View File

@ -1,13 +1,43 @@
mod accounts;
mod app;
mod ui;
mod cli;
pub mod event_handler;
#[cfg(feature = "tui")]
mod tui_app;
#[cfg(feature = "cli")]
mod repl;
use clap::Parser;
use crate::cli::{Args, Command};
#[cfg(feature = "tui")]
pub use tui_app::*;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
cli_log::init_cli_log!();
let mut app = app::App::new()?;
app.run().await?;
let args = Args::parse();
let command = args.subcommand.unwrap_or(
#[cfg(all(feature = "tui", not(feature = "cli")))]
Command::Start {},
#[cfg(all(feature = "cli", not(feature = "tui")))]
Command::Repl {},
#[cfg(all(feature = "cli", feature = "tui"))]
Command::Start {},
);
match command {
#[cfg(feature = "cli")]
Command::Repl {} => {
repl::run().await?;
}
#[cfg(feature = "tui")]
Command::Start {} => {
let mut app = app::App::new()?;
app.run().await?;
}
};
Ok(())
}

41
src/repl/mod.rs Normal file
View File

@ -0,0 +1,41 @@
use std::io::ErrorKind;
use anyhow::{Context, Result};
use cli_log::{info, warn};
use tokio::io::{stdin, stdout, AsyncReadExt, AsyncWriteExt};
pub async fn run() -> Result<()> {
let mut stdin = stdin();
let mut stdout = stdout();
let mut buffer = [0; 1];
let mut new_command = vec![];
loop {
stdout
.write("trinitrix:> ".as_bytes())
.await
.context("Failed to write prompt")?;
stdout.flush().await.context("Failed to flush prompt")?;
new_command.clear();
loop {
if let Err(err) = stdin.read_exact(&mut buffer).await {
if err.kind() == ErrorKind::UnexpectedEof {
warn!("Unexpected EOF, we assume the user quit.");
return Ok(());
} else {
Err(err).context("Failed to read next character")?;
}
}
if buffer == "\n".as_bytes() {
break;
} else {
new_command.append(&mut buffer.to_vec());
}
}
info!(
"Got user repl input: {}",
String::from_utf8(new_command.clone())
.context("Failed to convert user input to utf8 string")?
)
}
}

View File

@ -0,0 +1,63 @@
use cli_log::debug;
#[derive(Debug)]
pub enum Command {
Greet(String),
Exit,
CommandLineShow,
CommandLineHide,
CyclePlanes,
CyclePlanesRev,
RoomMessageSend(String),
Help(Option<String>),
}
pub fn generate_ci_functions(
lua: mlua::Lua,
tx: tokio::sync::mpsc::Sender<crate::app::events::event_types::Event>,
) -> 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<String, mlua::Error> {
let (callback_tx, mut callback_rx) = tokio::sync::mpsc::channel::<String>(256);
let tx: core::cell::Ref<tokio::sync::mpsc::Sender<crate::app::events::event_types::Event>> =
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"),
)));
}
}

View File

@ -2,14 +2,17 @@ pub mod command_interface;
pub mod events;
pub mod status;
use std::{path::Path, sync::Arc};
use std::path::Path;
use anyhow::{Context, Error, Result};
use cli_log::info;
use matrix_sdk::Client;
use mlua::Lua;
use once_cell::sync::OnceCell;
use status::{State, Status};
use tokio::sync::mpsc;
use tokio::{
sync::{mpsc, Mutex},
task::LocalSet,
};
use tokio_util::sync::CancellationToken;
use crate::{
@ -31,16 +34,42 @@ pub struct App<'ui> {
input_listener_killer: CancellationToken,
matrix_listener_killer: CancellationToken,
lua: Arc<Lua>,
lua_command_tx: mpsc::Sender<String>,
}
impl App<'_> {
pub fn new() -> Result<Self> {
fn set_up_lua(tx: mpsc::Sender<Event>) -> Arc<Lua> {
let mut lua = Lua::new();
fn set_up_lua(tx: mpsc::Sender<Event>) -> mpsc::Sender<String> {
info!("Setting up Lua context..");
static LUA: OnceCell<Mutex<mlua::Lua>> = OnceCell::new();
generate_ci_functions(&mut lua, tx);
Arc::new(lua)
let (lua_command_tx, mut rx) = mpsc::channel(256);
let local_set = LocalSet::new();
local_set.spawn_local(async move {
let lua = LUA
.get_or_init(|| Mutex::new(generate_ci_functions(mlua::Lua::new(), tx)))
.lock()
.await;
info!("Initialized Lua context");
while let Some(command) = rx.recv().await {
info!("Recieved command to execute: `{}`", &command);
let output = lua
.load(&command)
// FIXME this assumes string output only
.eval_async::<String>()
.await
.with_context(|| format!("Failed to execute: `{command}`"));
info!(
"Function `{}` returned: `{}`",
command,
output.unwrap_or("<returned error>".to_owned())
);
}
});
lua_command_tx
}
let path: &std::path::Path = Path::new("userdata/accounts.json");
@ -62,7 +91,7 @@ impl App<'_> {
input_listener_killer: CancellationToken::new(),
matrix_listener_killer: CancellationToken::new(),
lua: set_up_lua(tx),
lua_command_tx: set_up_lua(tx),
})
}
@ -86,11 +115,10 @@ impl App<'_> {
self.ui.update(&self.status).await?;
let event = self.rx.recv().await.context("Failed to get next event")?;
match event.handle(self).await? {
event_types::EventStatus::Ok => (),
event_types::EventStatus::Terminate => break,
_ => (),
_ => todo!(),
};
}

7
src/tui_app/mod.rs Normal file
View File

@ -0,0 +1,7 @@
pub mod app;
pub mod ui;
pub mod accounts;
pub use app::*;
pub use ui::*;
pub use accounts::*;