Compare commits

..

No commits in common. "7aea23f3b650820453970a625b92626f72042a34" and "d93b00484eea27f1ff853877012e6fc31f8d5004" have entirely different histories.

14 changed files with 766 additions and 713 deletions

652
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,15 +11,15 @@ default = ["tui"]
tui = ["dep:tui", "dep:tui-textarea", "dep:crossterm", "dep:tokio-util", "dep:serde", "dep:indexmap"] tui = ["dep:tui", "dep:tui-textarea", "dep:crossterm", "dep:tokio-util", "dep:serde", "dep:indexmap"]
[dependencies] [dependencies]
clap = { version = "4.4.2", features = ["derive"] } clap = { version = "4.3.19", features = ["derive"] }
cli-log = "2.0" cli-log = "2.0"
anyhow = "1.0" anyhow = "1.0"
matrix-sdk = "0.6" matrix-sdk = "0.6"
tokio = { version = "1.32", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.29", features = ["macros", "rt-multi-thread"] }
# lua stuff # lua stuff
language_macros = { path = "./language_macros" } language_macros = { path = "./language_macros" }
mlua = { version = "0.9.1", features = ["lua54", "async", "send", "serialize"] } mlua = { version = "0.8.9", features = ["lua54", "async", "send", "serialize"] }
once_cell = "1.18.0" once_cell = "1.18.0"
# tui feature specific parts # tui feature specific parts

View File

@ -16,11 +16,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1693762390, "lastModified": 1688772518,
"narHash": "sha256-IzmbGqrzVUzyc1IxgH62nQ9l6Dz7hq6JWIeyZiDZqKw=", "narHash": "sha256-ol7gZxwvgLnxNSZwFTDJJ49xVY5teaSvF7lzlo3YQfM=",
"owner": "ipetkov", "owner": "ipetkov",
"repo": "crane", "repo": "crane",
"rev": "7b92b595c947b3c82ba04f78e58446faac20c4cb", "rev": "8b08e96c9af8c6e3a2b69af5a7fa168750fcf88e",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -50,11 +50,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1692799911, "lastModified": 1689068808,
"narHash": "sha256-3eihraek4qL744EvQXsK1Ha6C3CR7nnT8X2qWap4RNk=", "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "f9e7cf818399d17d347f847525c5a5a8032e4e44", "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -65,11 +65,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1693654884, "lastModified": 1690327932,
"narHash": "sha256-EqKKEl+IOS8TSjkt+xn1qGpsjnx5/ag33YNQ1+c7OuM=", "narHash": "sha256-Fv7PYZxN4eo0K6zXhHG/vOc+e2iuqQ5ywDrh0yeRjP0=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "e7f35e03abd06a2faef6684d0de813370e13bda8", "rev": "a9b47d85504bdd199e90846622c76aa0bfeabfac",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -100,11 +100,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1693707092, "lastModified": 1690338181,
"narHash": "sha256-HR1EnynBSPqbt+04/yxxqsG1E3n6uXrOl7SPco/UnYo=", "narHash": "sha256-Sz2oQ9aNS3MVncnCMndr0302G26UrFUfPynoH2iLjsg=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "98ccb73e6eefc481da6039ee57ad8818d1ca8d56", "rev": "b7f0b7b58b3c6f14a1377ec31a3d78b23ab843ec",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -116,11 +116,11 @@
"rustc_cranelift_backend": { "rustc_cranelift_backend": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1693573696, "lastModified": 1690205271,
"narHash": "sha256-9V2JGtm7tiAAxaIMXzHLliS7rRyuogMxCmP8u9VRjBE=", "narHash": "sha256-mMj1dSlGzM+jCgtVX/KNxdYPXohS22RyU/uWmhR7EA0=",
"owner": "bjorn3", "owner": "bjorn3",
"repo": "rustc_codegen_cranelift", "repo": "rustc_codegen_cranelift",
"rev": "0559de65672243a97d6cd6e6ee6a8e8c291ef4ce", "rev": "6641b3a548a425eae518b675e43b986094daf609",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -133,7 +133,7 @@
"rustc_cranelift_backend_src": { "rustc_cranelift_backend_src": {
"flake": false, "flake": false,
"locked": { "locked": {
"narHash": "sha256-h7VLPtyiXg5KpbQKPo3RKl/+AnTWqimKqAXc1VngYEo=", "narHash": "sha256-E4n1Dqne8/9XHVKFqApdp2baxXun0DMl+oHfo4+oq3Q=",
"type": "tarball", "type": "tarball",
"url": "https://github.com/bjorn3/rustc_codegen_cranelift/releases/download/dev/cg_clif-x86_64-unknown-linux-gnu.tar.xz" "url": "https://github.com/bjorn3/rustc_codegen_cranelift/releases/download/dev/cg_clif-x86_64-unknown-linux-gnu.tar.xz"
}, },

View File

@ -52,28 +52,6 @@
... ...
}: }:
flake-utils.lib.eachDefaultSystem (system: let flake-utils.lib.eachDefaultSystem (system: let
inherit (pkgs) lib;
bintools-wrapper = "${nixpkgs}/pkgs/build-support/bintools-wrapper";
mold' = pkgs.symlinkJoin {
name = "mold";
paths = [pkgs.mold];
nativeBuildInputs = [pkgs.makeWrapper];
suffixSalt = lib.replaceStrings ["-" "."] ["_" "_"] pkgs.targetPlatform.config;
postBuild = ''
for bin in ${pkgs.mold}/bin/*; do
rm $out/bin/"$(basename "$bin")"
export prog="$bin"
substituteAll "${bintools-wrapper}/ld-wrapper.sh" $out/bin/"$(basename "$bin")"
chmod +x $out/bin/"$(basename "$bin")"
mkdir -p $out/nix-support
substituteAll "${bintools-wrapper}/add-flags.sh" $out/nix-support/add-flags.sh
substituteAll "${bintools-wrapper}/add-hardening.sh" $out/nix-support/add-hardening.sh
substituteAll "${bintools-wrapper}/../wrapper-common/utils.bash" $out/nix-support/utils.bash
done
'';
};
c_rust = pkgs.rust-bin.fromRustupToolchainFile "${rustc_cranelift_backend}/rust-toolchain"; c_rust = pkgs.rust-bin.fromRustupToolchainFile "${rustc_cranelift_backend}/rust-toolchain";
rcb = pkgs.stdenv.mkDerivation { rcb = pkgs.stdenv.mkDerivation {
pname = "rustc_cranelift_backend"; pname = "rustc_cranelift_backend";
@ -168,7 +146,7 @@
nativeBuildInputs = with pkgs; [ nativeBuildInputs = with pkgs; [
pkg-config pkg-config
mold' mold
rcb rcb
]; ];
buildInputs = with pkgs; [ buildInputs = with pkgs; [

View File

@ -126,7 +126,67 @@ fn get_function_body(field: &Field, has_input: bool, output_type: &Option<Type>)
let function_return = if let Some(_) = output_type { let function_return = if let Some(_) = output_type {
quote! { quote! {
return Ok(output.into_lua(lua).expect("This conversion should always work")); let converted_output = lua
.to_value(&output)
.expect("This conversion should (indirectely) be checked at compile time");
if let mlua::Value::Table(table) = converted_output {
let real_output: mlua::Value = match output {
CommandTransferValue::Nil => table
.get("Nil")
.expect("This should exist"),
CommandTransferValue::Boolean(_) => table
.get("Boolean")
.expect("This should exist"),
CommandTransferValue::Integer(_) => table
.get("Integer")
.expect("This should exist"),
CommandTransferValue::Number(_) => table
.get("Number")
.expect("This should exist"),
CommandTransferValue::String(_) => table
.get("String")
.expect("This should exist"),
CommandTransferValue::Table(_) => {
todo!()
// FIXME(@Soispha): This returns a table with the values wrapped the
// same way the values above are wrapped. That is (from the greet_multiple
// function):
// ```json
// {
// "Table": {
// "UserName1": {
// "Integer": 2
// }
// }
// }
// ```
// whilst the output should be:
// ```json
// {
// "UserName1": 2
// }
// ```
// That table would need to be unpacked, but this requires some recursive
// function, which seems not very performance oriented.
//
// My first (quick) attempt:
//let mut output_table = lua.create_table().expect("This should work?");
//let initial_table: mlua::Value = table
// .get("Table")
// .expect("This should exist");
//while let mlua::Value::Table(table) = initial_table {
// for pair in table.pairs() {
// let (key, value) = pair.expect("This should also work?");
// output_table.set(key, value);
// }
//}
},
};
return Ok(real_output);
} else {
unreachable!("Lua serializes these things always in a table");
}
} }
} else { } else {
quote! { quote! {
@ -136,7 +196,7 @@ fn get_function_body(field: &Field, has_input: bool, output_type: &Option<Type>)
let does_function_expect_output = if output_type.is_some() { let does_function_expect_output = if output_type.is_some() {
quote! { quote! {
// We didn't receive output but expected output. Raise an error to notify the lua code // We didn't receive output but expected output. Raise an error to notify the lua code
// about it. // about it
return Err(mlua::Error::ExternalError(std::sync::Arc::new( return Err(mlua::Error::ExternalError(std::sync::Arc::new(
err err
))); )));
@ -150,7 +210,7 @@ fn get_function_body(field: &Field, has_input: bool, output_type: &Option<Type>)
quote! { quote! {
let (callback_tx, callback_rx) = tokio::sync::oneshot::channel::<CommandTransferValue>(); let (callback_tx, callback_rx) = tokio::sync::oneshot::channel::<CommandTransferValue>();
let tx: mlua::AppDataRef<tokio::sync::mpsc::Sender<Event>> = let tx: core::cell::Ref<tokio::sync::mpsc::Sender<Event>> =
lua.app_data_ref().expect("This should exist, it was set before"); lua.app_data_ref().expect("This should exist, it was set before");
(*tx) (*tx)

View File

@ -1,94 +0,0 @@
use cli_log::{debug, info};
use mlua::{IntoLua, LuaSerdeExt, Table, Value};
use super::CommandTransferValue;
impl<'lua> IntoLua<'lua> for CommandTransferValue {
fn into_lua(self, lua: &'lua mlua::Lua) -> mlua::Result<mlua::Value<'lua>> {
let converted_output = lua.to_value(&self)?;
return unwrap(converted_output, lua);
}
}
fn unwrap<'lua>(
value_to_unwrap: Value<'lua>,
lua: &'lua mlua::Lua,
) -> mlua::Result<mlua::Value<'lua>> {
fn unwrap_first_level<'lua>(table: Table<'lua>) -> mlua::Result<Value<'lua>> {
let (_, value): (Value, Value) = table
.pairs()
.next()
.expect("Exactly one item should extist")?;
Ok(value)
}
// That converted value looks somewhat like this (e.g. a String):
// ```
// {
// ["String"] = "hi",
// }
// ```
// or like this (e.g. a table):
// ```
// {
// ["Table"] = {
// ["UserId"] = {
// ["Integer"] = 2,
// },
// ["UserName"] = {
// ["String"] = "James",
// },
// },
// }
// ```
if let Value::Table(table) = value_to_unwrap {
let value = unwrap_first_level(table)?;
if let Value::Table(wrapped_table) = value {
info!("We've got a wtable! wtable: \n{:#?}", wrapped_table);
// we now have a wrapped table value for example like this:
// ```
// {
// ["UserId"] = {
// ["Integer"] = 2,
// },
// ["UserName"] = {
// ["String"] = "James",
// },
// ["Versions"] = {
// ["Table"] = {
// ["api"] = {
// ["Boolean"] = true,
// },
// ["interface"] = {
// ["Integer"] = 3,
// },
// },
// },
// }
// ```
let output_table: Table = lua
.load("{}")
.eval()
.expect("This is static, it should always work");
// FIXME(@soispha): This still fails for nested tables (i.e. the table above), as it
// unpacks too much. While unpacking the while loop should stop, when a key is not from
// the CommandTransferValue family (i.e. ["Integer", "Boolean", "String", "Table",
// etc.]) <2023-09-09>
for pair in wrapped_table.pairs::<Value, Value>() {
let (key, mut raw_value) = pair?;
while let Value::Table(raw_table) = raw_value {
raw_value = unwrap_first_level(raw_table)?;
}
output_table.set(key, raw_value)?;
}
return Ok(output_table.into_lua(lua)?);
} else {
info!("We've got a normal output! output: {:#?}", value);
// we had a simple wrapped value, which is already unwrapped, thus it can be
// returned directly
return Ok(value);
}
} else {
unreachable!("The returned table should always only contain one element");
}
}

View File

@ -1,60 +0,0 @@
use std::{collections::HashMap, fmt::Display};
use serde::{Deserialize, Serialize};
pub mod type_conversions;
// language support
pub mod lua;
pub type Table = HashMap<String, CommandTransferValue>;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum CommandTransferValue {
/// `nil` or `null` or `undefined`; anything which goes in that group of types.
Nil,
/// `true` or `false`.
Boolean(bool),
// A “light userdata” object, equivalent to a raw pointer.
// /* TODO */ LightUserData(LightUserData),
/// An integer number.
Integer(i64),
/// A floating point number.
Number(f64),
/// A string
String(String),
/// A table, dictionary or HashMap
Table(HashMap<String, CommandTransferValue>),
// Reference to a Lua function (or closure).
// /* TODO */ Function(Function),
// Reference to a Lua thread (or coroutine).
// /* TODO */ Thread(Thread<'lua>),
// Reference to an userdata object that holds a custom type which implements `UserData`.
// Special builtin userdata types will be represented as other `Value` variants.
// /* TODO */ UserData(AnyUserData),
// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
// /* TODO */ Error(Error),
}
impl Display for CommandTransferValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CommandTransferValue::Nil => f.write_str("Nil"),
CommandTransferValue::Boolean(bool) => f.write_str(&format!("{}", bool)),
CommandTransferValue::Integer(int) => f.write_str(&format!("{}", int)),
CommandTransferValue::Number(num) => f.write_str(&format!("{}", num)),
CommandTransferValue::String(str) => f.write_str(&format!("{}", str)),
// TODO(@Soispha): The following line should be a real display call, but how do you
// format a HashMap?
CommandTransferValue::Table(table) => f.write_str(&format!("{:#?}", table)),
}
}
}

View File

@ -1,34 +0,0 @@
use std::collections::HashMap;
use super::CommandTransferValue;
impl From<String> for CommandTransferValue {
fn from(s: String) -> Self {
Self::String(s.to_owned())
}
}
impl From<f64> for CommandTransferValue {
fn from(s: f64) -> Self {
Self::Number(s.to_owned())
}
}
impl From<i64> for CommandTransferValue {
fn from(s: i64) -> Self {
Self::Integer(s.to_owned())
}
}
impl From<HashMap<String, CommandTransferValue>> for CommandTransferValue {
fn from(s: HashMap<String, CommandTransferValue>) -> Self {
Self::Table(s.to_owned())
}
}
impl From<bool> for CommandTransferValue {
fn from(s: bool) -> Self {
Self::Boolean(s.to_owned())
}
}
impl From<()> for CommandTransferValue {
fn from(_: ()) -> Self {
Self::Nil
}
}

View File

@ -1,9 +1,10 @@
use std::thread; use std::{collections::HashMap, fmt::Display, thread};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use cli_log::{debug, error, info}; use cli_log::{error, info, debug};
use mlua::{Function, Value}; use mlua::{Function, Value};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use serde::{Deserialize, Serialize};
use tokio::{ use tokio::{
runtime::Builder, runtime::Builder,
sync::{mpsc, Mutex}, sync::{mpsc, Mutex},
@ -16,13 +17,95 @@ use crate::app::{
}; };
static LUA: OnceCell<Mutex<mlua::Lua>> = OnceCell::new(); static LUA: OnceCell<Mutex<mlua::Lua>> = OnceCell::new();
pub type Table = HashMap<String, CommandTransferValue>;
#[derive(Debug, Serialize, Deserialize, Clone)]
pub enum CommandTransferValue {
/// `nil` or `null` or `undefined`; anything which goes in that group of types.
Nil,
/// `true` or `false`.
Boolean(bool),
// A "light userdata" object, equivalent to a raw pointer.
// /*TODO*/ LightUserData(LightUserData),
/// An integer number.
Integer(i64),
/// A floating point number.
Number(f64),
/// A string
String(String),
/// A table, directory or HashMap
Table(HashMap<String, CommandTransferValue>),
// Reference to a Lua function (or closure).
// /* TODO */ Function(Function),
// Reference to a Lua thread (or coroutine).
// /* TODO */ Thread(Thread<'lua>),
// Reference to a userdata object that holds a custom type which implements `UserData`.
// Special builtin userdata types will be represented as other `Value` variants.
// /* TODO */ UserData(AnyUserData),
// `Error` is a special builtin userdata type. When received from Lua it is implicitly cloned.
// /* TODO */ Error(Error),
}
impl Display for CommandTransferValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CommandTransferValue::Nil => f.write_str("Nil"),
CommandTransferValue::Boolean(bool) => f.write_str(&format!("{}", bool)),
CommandTransferValue::Integer(int) => f.write_str(&format!("{}", int)),
CommandTransferValue::Number(num) => f.write_str(&format!("{}", num)),
CommandTransferValue::String(str) => f.write_str(&format!("{}", str)),
// TODO(@Soispha): The following line should be a real display call, but how do you
// format a HashMap?
CommandTransferValue::Table(table) => f.write_str(&format!("{:#?}", table)),
}
}
}
/// This structure contains the necessary state for running an embedded Lua runtime (i.e.
/// the tread, the Lua memory, etc.).
pub struct LuaCommandManager { pub struct LuaCommandManager {
lua_command_tx: mpsc::Sender<String>, lua_command_tx: mpsc::Sender<String>,
} }
impl From<String> for CommandTransferValue {
fn from(s: String) -> Self {
Self::String(s.to_owned())
}
}
impl From<f64> for CommandTransferValue {
fn from(s: f64) -> Self {
Self::Number(s.to_owned())
}
}
impl From<i64> for CommandTransferValue {
fn from(s: i64) -> Self {
Self::Integer(s.to_owned())
}
}
impl From<HashMap<String, CommandTransferValue>> for CommandTransferValue {
fn from(s: HashMap<String, CommandTransferValue>) -> Self {
Self::Table(s.to_owned())
}
}
impl From<bool> for CommandTransferValue {
fn from(s: bool) -> Self {
Self::Boolean(s.to_owned())
}
}
impl From<()> for CommandTransferValue {
fn from(_: ()) -> Self {
Self::Nil
}
}
impl LuaCommandManager { impl LuaCommandManager {
pub async fn execute_code(&self, code: String) { pub async fn execute_code(&self, code: String) {
self.lua_command_tx self.lua_command_tx
@ -46,11 +129,13 @@ impl LuaCommandManager {
waiting for commands.." waiting for commands.."
); );
while let Some(command) = lua_command_rx.recv().await { while let Some(command) = lua_command_rx.recv().await {
debug!("Recieved lua code (in LuaCommandHandler): {}", &command); debug!("Recieved lua code: {}", &command);
let local_event_call_tx = event_call_tx.clone(); let local_event_call_tx = event_call_tx.clone();
task::spawn_local(async move { task::spawn_local(async move {
exec_lua(&command, local_event_call_tx).await.expect( exec_lua_command(&command, local_event_call_tx)
.await
.expect(
"This should return all relevent errors \ "This should return all relevent errors \
by other messages, \ by other messages, \
this should never error", this should never error",
@ -65,7 +150,7 @@ impl LuaCommandManager {
} }
} }
async fn exec_lua(lua_code: &str, event_call_tx: mpsc::Sender<Event>) -> Result<()> { async fn exec_lua_command(command: &str, event_call_tx: mpsc::Sender<Event>) -> Result<()> {
let second_event_call_tx = event_call_tx.clone(); let second_event_call_tx = event_call_tx.clone();
let lua = LUA let lua = LUA
.get_or_init(|| { .get_or_init(|| {
@ -77,23 +162,21 @@ async fn exec_lua(lua_code: &str, event_call_tx: mpsc::Sender<Event>) -> Result<
.lock() .lock()
.await; .await;
info!("Recieved code to execute: `{}`, executing...", &lua_code); info!("Recieved code to execute: `{}`, executing...", &command);
let output = lua.load(lua_code).eval_async::<Value>().await; let output = lua.load(command).eval_async::<Value>().await;
match output { match output {
Ok(out) => { Ok(out) => {
let to_string_fn: Function = lua.globals().get("tostring").expect("This always exists"); let to_string_fn: Function = lua.globals().get("tostring").expect("This always exists");
let output: String = to_string_fn.call(out).expect("tostring should not error"); let output: String = to_string_fn.call(out).expect("tostring should not error");
info!("Lua code `{}` evaluated to: `{}`", lua_code, &output); info!("Function `{}` returned: `{}`", command, &output);
if output != "nil" {
event_call_tx event_call_tx
.send(Event::CommandEvent(Command::DisplayOutput(output), None)) .send(Event::CommandEvent(Command::DisplayOutput(output), None))
.await .await
.context("Failed to send lua output command")? .context("Failed to send lua output command")?
} }
}
Err(err) => { Err(err) => {
error!("Lua code `{}` returned error: `{}`", lua_code, err); error!("Function `{}` returned error: `{}`", command, err);
event_call_tx event_call_tx
.send(Event::CommandEvent( .send(Event::CommandEvent(
Command::RaiseError(err.to_string()), Command::RaiseError(err.to_string()),

View File

@ -1,15 +1,14 @@
// Use `cargo expand app::command_interface` for an overview of the file contents // Use `cargo expand app::command_interface` for an overview of the file contents
pub mod command_transfer_value;
pub mod lua_command_manager; pub mod lua_command_manager;
use language_macros::ci_command_enum; use language_macros::ci_command_enum;
// TODO(@Soispha): Should these paths be moved to the proc macro? // TODO(@Soispha): Should these paths be moved to the proc macro?
// As they are not static, it could be easier for other people, // As they are not static, it could be easier for other people,
// if they stay here. // if they stay here
use crate::app::command_interface::command_transfer_value::CommandTransferValue; use lua_command_manager::CommandTransferValue;
use mlua::IntoLua; use mlua::LuaSerdeExt;
use crate::app::Event; use crate::app::Event;
#[ci_command_enum] #[ci_command_enum]
@ -26,6 +25,7 @@ struct Commands {
/// Returns a table of greeted users /// Returns a table of greeted users
greet_multiple: fn() -> Table, greet_multiple: fn() -> Table,
// End debug functions // End debug functions
/// Closes the application /// Closes the application
exit: fn(), exit: fn(),
@ -46,7 +46,7 @@ struct Commands {
set_mode_insert: fn(), set_mode_insert: fn(),
/// Send a message to the current room /// Send a message to the current room
/// The send message is interpreted literally. /// The sent message is interpreted literally.
room_message_send: fn(String), room_message_send: fn(String),
/// Open the help pages at the first occurrence of /// Open the help pages at the first occurrence of

View File

@ -4,18 +4,14 @@ use anyhow::{Error, Result};
use cli_log::{trace, warn}; use cli_log::{trace, warn};
use tokio::sync::oneshot; use tokio::sync::oneshot;
use crate::{ use crate::{app::{
app::{
command_interface::{ command_interface::{
command_transfer_value::{CommandTransferValue, Table}, lua_command_manager::{CommandTransferValue, Table},
Command, Command,
}, },
events::event_types::EventStatus, events::event_types::EventStatus,
status::State, App, status::State,
App, }, ui::central::InputPosition};
},
ui::central::InputPosition,
};
pub async fn handle( pub async fn handle(
app: &mut App<'_>, app: &mut App<'_>,
@ -24,8 +20,8 @@ pub async fn handle(
) -> Result<EventStatus> { ) -> Result<EventStatus> {
// A command can both return _status output_ (what you would normally print to stderr) // A command can both return _status output_ (what you would normally print to stderr)
// and _main output_ (the output which is normally printed to stdout). // and _main output_ (the output which is normally printed to stdout).
// We simulate these by returning the main output to the Lua function, and printing the // We simulate these by returning the main output to the lua function, and printing the
// status output to a status UI field. // status output to a status ui field.
// //
// Every function should return some status output to show the user, that something is // Every function should return some status output to show the user, that something is
// happening, while only some functions return some value to the main output, as this // happening, while only some functions return some value to the main output, as this
@ -72,7 +68,7 @@ pub async fn handle(
} }
Command::DisplayOutput(output) => { Command::DisplayOutput(output) => {
// TODO(@Soispha): This is only used to show the Lua command output to the user. // TODO(@Soispha): This is only used to show the lua command output to the user.
// Lua commands already receive the output. This should probably be communicated // Lua commands already receive the output. This should probably be communicated
// better, should it? // better, should it?
send_status_output!(output); send_status_output!(output);
@ -81,7 +77,6 @@ pub async fn handle(
Command::CommandLineShow => { Command::CommandLineShow => {
app.ui.cli_enable(); app.ui.cli_enable();
app.status.set_state(State::Command);
send_status_output!("CLI online"); send_status_output!("CLI online");
EventStatus::Ok EventStatus::Ok
} }
@ -119,7 +114,7 @@ pub async fn handle(
room.send(msg.clone()).await?; room.send(msg.clone()).await?;
send_status_output!("Sent message: `{}`", msg); send_status_output!("Sent message: `{}`", msg);
} else { } else {
// TODO(@Soispha): Should this raise a Lua error? It could be very confusing, // TODO(@Soispha): Should this raise a lua error? It could be very confusing,
// when a user doesn't read the log. // when a user doesn't read the log.
warn!("Can't send message: `{}`, as there is no open room!", &msg); warn!("Can't send message: `{}`, as there is no open room!", &msg);
} }
@ -131,25 +126,13 @@ pub async fn handle(
} }
Command::Print(output) => { Command::Print(output) => {
// FIXME(@Soispha): This only works with strings, which is a clear downside to the // FIXME(@Soispha): This only works with strings, which is a clear downside to the
// original print function. Find a way to just use the original one. // original print function. Find a way to just use the original one
send_main_output!("{}", output); send_main_output!("{}", output);
EventStatus::Ok EventStatus::Ok
} }
Command::GreetMultiple => { Command::GreetMultiple => {
let mut table: Table = HashMap::new(); let mut table: Table = HashMap::new();
table.insert("UserId".to_owned(), CommandTransferValue::Integer(2)); table.insert("UserName1".to_owned(), CommandTransferValue::Integer(2));
table.insert(
"UserName".to_owned(),
CommandTransferValue::String("James".to_owned()),
);
let mut second_table: Table = HashMap::new();
second_table.insert("interface".to_owned(), CommandTransferValue::Integer(3));
second_table.insert("api".to_owned(), CommandTransferValue::Boolean(true));
table.insert(
"Versions".to_owned(),
CommandTransferValue::Table(second_table),
);
send_main_output!(table); send_main_output!(table);
EventStatus::Ok EventStatus::Ok
} }

View File

@ -10,56 +10,13 @@ use crate::{
ui::central, ui::central,
}; };
pub async fn handle_command( pub async fn handle_normal(
app: &mut App<'_>, app: &mut App<'_>,
input_event: &CrosstermEvent, input_event: &CrosstermEvent,
) -> Result<EventStatus> { ) -> Result<EventStatus> {
if let Some(cli) = &app.ui.cli {
match input_event { match input_event {
CrosstermEvent::Key(KeyEvent { CrosstermEvent::Key(KeyEvent {
code: KeyCode::Esc, .. code: KeyCode::Esc, ..
}) => {
app.tx
.send(Event::CommandEvent(Command::SetModeNormal, None))
.await?;
}
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Enter,
..
}) => {
let ci_event = cli
.lines()
.get(0)
.expect(
"One line always exists,
and others can't exists
because we collect on
enter",
)
.to_owned();
app.tx
.send(Event::LuaCommand(ci_event))
.await
.context("Failed to send lua command to internal event stream")?;
}
_ => {
app.ui
.cli
.as_mut()
.expect("This is already checked")
.input(tui_textarea::Input::from(input_event.to_owned()));
}
}
} else {
unreachable!("The cli should not be active while no cli is defined");
}
Ok(EventStatus::Ok)
}
pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<EventStatus> {
match input_event {
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Char('q'),
..
}) => { }) => {
app.tx app.tx
.send(Event::CommandEvent(Command::Exit, None)) .send(Event::CommandEvent(Command::Exit, None))
@ -180,7 +137,38 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
_ => (), _ => (),
}; };
} }
central::InputPosition::CLI => {
if let Some(cli) = &app.ui.cli {
match input {
CrosstermEvent::Key(KeyEvent {
code: KeyCode::Enter,
..
}) => {
let ci_event = cli
.lines()
.get(0)
.expect(
"One line always exists,
and others can't exists
because we collect on
enter",
)
.to_owned();
app.tx
.send(Event::LuaCommand(ci_event))
.await
.context("Failed to send lua command to internal event stream")?;
}
_ => {
app.ui
.cli
.as_mut()
.expect("This is already checked")
.input(tui_textarea::Input::from(input.to_owned()));
}
};
};
}
_ => (), _ => (),
}, },
}; };

View File

@ -6,7 +6,7 @@ use crossterm::event::Event as CrosstermEvent;
use tokio::sync::oneshot; use tokio::sync::oneshot;
use crate::app::{ use crate::app::{
command_interface::{command_transfer_value::CommandTransferValue, Command}, command_interface::{lua_command_manager::CommandTransferValue, Command},
status::State, status::State,
App, App,
}; };
@ -45,9 +45,6 @@ impl Event {
State::Insert => main::handle_insert(app, &event).await.with_context(|| { State::Insert => main::handle_insert(app, &event).await.with_context(|| {
format!("Failed to handle input (insert) event: `{:#?}`", event) format!("Failed to handle input (insert) event: `{:#?}`", event)
}), }),
State::Command => main::handle_command(app, &event).await.with_context(|| {
format!("Failed to handle input (command) event: `{:#?}`", event)
}),
State::Setup => setup::handle(app, &event).await.with_context(|| { State::Setup => setup::handle(app, &event).await.with_context(|| {
format!("Failed to handle input (setup) event: `{:#?}`", event) format!("Failed to handle input (setup) event: `{:#?}`", event)
}), }),

View File

@ -15,7 +15,6 @@ use matrix_sdk::{
pub enum State { pub enum State {
Normal, Normal,
Insert, Insert,
Command,
/// Temporary workaround until command based login is working /// Temporary workaround until command based login is working
Setup, Setup,
} }
@ -58,7 +57,6 @@ impl fmt::Display for State {
match self { match self {
Self::Normal => write!(f, "Normal"), Self::Normal => write!(f, "Normal"),
Self::Insert => write!(f, "Insert"), Self::Insert => write!(f, "Insert"),
Self::Command => write!(f, "Command"),
Self::Setup => write!(f, "Setup (!! workaround !!)"), Self::Setup => write!(f, "Setup (!! workaround !!)"),
} }
} }