fix(src): Adapt the code to compile with trixy

The term "to compile" was specifically chosen, as this code does nothing
more. I would honestly be surprised if it worked in this state.
This commit is contained in:
Benedikt Peetz 2024-05-03 21:25:09 +02:00
parent 6ef6bea61c
commit a39a0875a3
13 changed files with 439 additions and 348 deletions

View File

@ -0,0 +1,142 @@
/*
* Copyright (C) 2023 - 2024:
* The Trinitrix Project <soispha@vhack.eu, antifallobst@systemausfall.org>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of the Trixy crate for Trinitrix.
*
* Trixy is free software: you can redistribute it and/or modify
* it under the terms of the Lesser GNU General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* and the Lesser GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*/
//// Prints to the output, with a newline.
// HACK(@soispha): The stdlib Lua `print()` function has stdout as output hardcoded,
// redirecting stdout seems too much like a hack thus we are just redefining the print function
// to output to a controlled output. <2023-09-09>
// This is implemented only for lua
/* fn print(CommandTransferValue); */
mod trinitrix {
/// Language specific functions, which mirror the `trinitrix.api` namespace.
/// That is, if you have to choose between a `std` and a `api` function choose the `std`
/// one as it will most likely be more high-level and easier to use (as it isn't abstracted
/// over multiple languages). Feel free to drop down to the lower level api, if you feel
/// like that more, it should be as stable and user-oriented as the `std` functions
mod stdi {}
/// General API to change stuff in Trinitrix
mod api {
/// Closes the application
fn exit();
/// Send a message to the current room
/// The send message is interpreted literally.
fn room_message_send(message: String);
//// Open the help pages at the first occurrence of
//// the input string if it is Some, otherwise open
//// the help pages at the start
// TODO(@soispha): To be implemented <2024-03-09>
// fn help(Option<String>);
//// Register a function to be used with the Trinitrix api
// (This function is not actually implemented here)
/* declare register_function: false, */
/// Function that change the UI, or UI state
mod ui {
enum Mode {
/// Default mode (navigation mode)
Normal,
/// Allows you to insert things
Insert,
/// actives the command line
Command,
}
/// Change the active mode
fn set_mode(mode: Mode);
/// Go to the next plane
fn cycle_planes();
/// Go to the previous plane
fn cycle_planes_rev();
}
/// Manipulate keymappings, the mode is specified as a String build up of all mode
/// the keymapping should be active in. The mapping works as follows:
/// n => normal Mode
/// c => command Mode
/// i => insert Mode
///
/// The key works in a similar matter, specifying the required keypresses to trigger the
/// callback. For example "aba" for require the user to press "a" then "b" then "a" again
/// to trigger the mapping. Special characters are encoded as follows:
/// "<C-a>ba" => "Ctrl+a" then "b" then "a"
/// "<S-a>" => "A" or "Shift+a"
/// "A" => "A"
/// "<M-a> " => "Alt+a" (<A-a>) or "Meta+a"(<M-a>) (most terminals can't really differentiate between these characters)
/// "a<C-b><C-a>" => "a" then "Ctrl+b" then "Ctrl+a" (also works for Shift, Alt and Super)
/// "<CSM-b>" => "Ctrl+Shift+Alt+b" (the ordering doesn't matter)
/// "a " => "a" then a literal space (" ")
/// "å🙂" => "å" then "🙂" (full Unicode support!)
/// "<ESC>" => escape key
/// "<F3>" => F3 key
/// "<BACKSPACE>" => backspace key (and so forth)
/// "<DASH>" => a literal "-"
/// "<ANGULAR_BRACKET_OPEN>" or "<ABO>" => a literal "<"
/// "<ANGULAR_BRACKET_CLOSE>" or "<ABC>" => a literal ">"
///
/// The callback MUST be registered first by calling
/// `trinitrix.api.register_function()` the returned value can than be used to
/// set the keymap.
mod keymaps {
/// Add a new keymapping
fn add(mode: String, key: String, callback: fn());
/// Remove a keymapping
///
/// Does nothing, if the keymapping doesn't exists yet
fn remove(mode: String, key: String);
/// List declared keymappings
fn get(mode: String);
}
/// Functions only used internally within Trinitrix
mod raw {
/// Send an error to the default error output
fn raise_error(error_message: String);
/// Send output to the default output
/// This is mainly used to display the final
/// output of evaluated lua commands.
fn display_output(output_message: String);
/// Input a character without checking for possible keymaps
/// If the current state does not expect input, this character is ignored
/// The encoding is the same as in the `trinitrix.api.keymaps` commands
fn send_input_unprocessed(input: String);
/// This namespace is used to store some command specific data (like functions, as
/// ensuring memory locations stay allocated in garbage collected language is hard)
///
/// Treat it as an implementation detail
mod __private {}
}
}
}
// Trixy is sort of a subset of rust
// vim: syntax=rust

View File

@ -1,150 +1,14 @@
// Use `cargo expand app::command_interface::command_list` for an overview of the file contents // Run the `api` bin to see the generated api
use language_macros::parse_command_enum; use crate::app::{events::Event, COMMAND_TRANSMITTER};
// TODO(@soispha): Should these paths be moved to the proc macro? include!(concat!(env!("OUT_DIR"), "/api.rs"));
// As they are not static, it could be easier for other people,
// if they stay here.
use crate::app::command_interface::command_transfer_value::{
support_types::Function, CommandTransferValue,
};
use crate::app::Event;
use mlua::IntoLua;
parse_command_enum! { pub fn handle_cmd(cmd: Commands) {
commands { let tx = COMMAND_TRANSMITTER
/// Prints to the output, with a newline. .get()
// HACK(@soispha): The stdlib Lua `print()` function has stdout as output hardcoded, .expect("The cell should always be populated, at this point");
// redirecting stdout seems too much like a hack thus we are just redefining the print function
// to output to a controlled output. <2023-09-09>
declare print: fn(CommandTransferValue),
namespace trinitrix { // FIXME: The None here is definitely wrong <2024-05-03>
/// Language specific functions, which mirror the `trinitrix.api` namespace. tx.send(Event::CommandEvent(cmd, None));
/// That is, if you have to choose between a `std` and a `api` function choose the `std`
/// one as it will most likely be more high-level and easier to use (as it isn't abstracted
/// over multiple languages). Feel free to drop down to the lower level api, if you feel
/// like that more, it should be as stable and user-oriented as the `std` functions
namespace std {
/// This command is a no-op, it's just here to ensure that the 'std'
/// namespace get actually created
// FIXME(@soispha): Add an attribute to namespaces to avoid having to use
// empty functions <2023-10-14>
declare private_initializer_std: fn(),
},
/// Debug only functions, these are effectively useless
namespace debug {
/// Greets the user
declare greet: fn(String) -> String,
/// Returns a table of greeted users
declare greet_multiple: fn() -> Table,
},
/// General API to change stuff in Name
namespace api {
/// Closes the application
declare exit: fn(),
/// Send a message to the current room
/// The send message is interpreted literally.
declare room_message_send: fn(String),
/// Open the help pages at the first occurrence of
/// the input string if it is Some, otherwise open
/// the help pages at the start
declare help: fn(Option<String>),
// Register a function to be used with the Trinitrix api
// (This function is not actually implemented here)
/* declare register_function: false, */
/// Function that change the UI, or UI state
namespace ui {
/// Shows the command line
declare command_line_show: fn(),
/// Hides the command line
declare command_line_hide: fn(),
/// Go to the next plane
declare cycle_planes: fn(),
/// Go to the previous plane
declare cycle_planes_rev: fn(),
/// Sets the current app mode to Normal / navigation mode
declare set_mode_normal: fn(),
/// Sets the current app mode to Insert / editing mode
declare set_mode_insert: fn(),
},
/// Manipulate keymappings, the mode is specified as a String build up of all mode
/// the keymapping should be active in. The mapping works as follows:
/// n => normal Mode
/// c => command Mode
/// i => insert Mode
///
/// The key works in a similar matter, specifying the required keypresses to trigger the
/// callback. For example "aba" for require the user to press "a" then "b" then "a" again
/// to trigger the mapping. Special characters are encoded as follows:
/// "<C-a>ba" => "Ctrl+a" then "b" then "a"
/// "<S-a>" => "A" or "Shift+a"
/// "A" => "A"
/// "<M-a> " => "Alt+a" (<A-a>) or "Meta+a"(<M-a>) (most terminals can't really differentiate between these characters)
/// "a<C-b><C-a>" => "a" then "Ctrl+b" then "Ctrl+a" (also works for Shift, Alt and Super)
/// "<CSM-b>" => "Ctrl+Shift+Alt+b" (the ordering doesn't matter)
/// "a " => "a" then a literal space (" ")
/// "å🙂" => "å" then "🙂" (full Unicode support!)
/// "<ESC>" => escape key
/// "<F3>" => F3 key
/// "<BACKSPACE>" => backspace key (and so forth)
/// "<DASH>" => a literal "-"
/// "<ANGULAR_BRACKET_OPEN>" or "<ABO>" => a literal "<"
/// "<ANGULAR_BRACKET_CLOSE>" or "<ABC>" => a literal ">"
///
/// The callback MUST be registered first by calling
/// `trinitrix.api.register_function()` the returned value can than be used to
/// set the keymap.
namespace keymaps {
/// Add a new keymapping
declare add: fn((/* mode: */ String, /* key: */ String, /* callback: */ Function)),
/// Remove a keymapping
///
/// Does nothing, if the keymapping doesn't exists
declare remove: fn((/* mode: */ String, /* key: */ String)),
/// List declared keymappings
declare get: fn(/* mode: */ String),
},
/// Functions only used internally within Trinitrix
namespace raw {
/// Send an error to the default error output
declare raise_error: fn(String),
/// Send output to the default output
/// This is mainly used to display the final
/// output of evaluated lua commands.
declare display_output: fn(String),
/// Input a character without checking for possible keymaps
/// If the current state does not expect input, this character is ignored
/// The encoding is the same as in the `trinitrix.api.keymaps` commands
declare send_input_unprocessed: fn(String),
/// This namespace is used to store some command specific data (like functions, as
/// ensuring memory locations stay allocated in garbage collected language is hard)
///
/// Treat it as an implementation detail
namespace __private {
/// This command is a no-op, it's just here to ensure that the '__private'
/// namespace get actually created
// FIXME(@soispha): Add an attribute to namespaces to avoid having to use
// empty functions <2023-10-14>
declare private_initializer_private: fn(),
},
},
},
},
}
} }

View File

@ -1,5 +1,5 @@
pub mod command_list; pub mod command_list;
pub mod command_transfer_value; // pub mod command_transfer_value;
pub mod lua_command_manager; // pub mod lua_command_manager;
pub use command_list::*; pub use command_list::*;

View File

@ -1,31 +1,36 @@
use std::{collections::HashMap, str::FromStr}; use std::{mem, str::FromStr};
use anyhow::{Error, Result}; use crate::{
app::{
command_interface::{
trinitrix::{
api::{keymaps::Keymaps, raw::Raw, ui::Ui, Api},
Trinitrix,
},
Commands,
},
events::EventStatus,
status::State,
App,
},
trinitrix::api::ui::Mode,
ui::central::InputPosition,
};
use anyhow::Result;
use cli_log::{info, trace, warn}; use cli_log::{info, trace, warn};
use crossterm::event::Event; use crossterm::event::Event;
use keymaps::{ use keymaps::{
key_repr::{Key, Keys}, key_repr::{Key, Keys},
trie::Node, trie::Node,
}; };
use tokio::sync::oneshot; use trixy::oneshot;
use crate::{
app::{
command_interface::{
command_transfer_value::{CommandTransferValue, Table},
Api, Command, Debug, Keymaps, Raw, Trinitrix, Ui,
},
events::EventStatus,
status::State,
App,
},
ui::central::InputPosition,
};
pub async fn handle( pub async fn handle(
app: &mut App<'_>, app: &mut App<'_>,
command: &Command, command: &Commands,
output_callback: Option<oneshot::Sender<CommandTransferValue>>,
// FIXME(@soispha): The `String` is temporary <2024-05-03>
output_callback: Option<oneshot::Sender<String>>,
) -> 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).
@ -51,110 +56,90 @@ pub async fn handle(
app.status.add_error_message(format!($str, $($args),+)) app.status.add_error_message(format!($str, $($args),+))
}; };
} }
macro_rules! send_main_output { // macro_rules! send_main_output {
($str:expr) => { // ($str:expr) => {
if let Some(sender) = output_callback { // if let Some(sender) = output_callback {
sender // sender
.send(CommandTransferValue::from($str)) // .send(CommandTransferValue::from($str))
.map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?; // .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
} // }
}; // };
($str:expr, $($args:ident),+) => { // ($str:expr, $($args:ident),+) => {
if let Some(sender) = output_callback { // if let Some(sender) = output_callback {
sender // sender
.send(CommandTransferValue::from(format!($str, $($args),+))) // .send(CommandTransferValue::from(format!($str, $($args),+)))
.map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?; // .map_err(|e| Error::msg(format!("Failed to send command main output: `{}`", e)))?;
} // }
}; // };
} // }
trace!("Handling command: {:#?}", command); trace!("Handling command: {:#?}", command);
Ok(match command { Ok(match command {
Command::Print(output) => { Commands::Trinitrix(trinitrix) => match trinitrix {
let output_str: String = output.to_string(); Trinitrix::Stdi(_) => {
send_status_output!(output_str); // No-op I guess
EventStatus::Ok EventStatus::Ok
} }
Command::Trinitrix(trinitrix) => match trinitrix {
Trinitrix::Debug(debug) => match debug {
Debug::Greet(msg) => {
send_main_output!("Greeting, {}!", msg);
EventStatus::Ok
}
Debug::GreetMultiple => {
let mut table: Table = HashMap::new();
table.insert("UserId".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);
EventStatus::Ok
}
},
Trinitrix::Api(api) => match api { Trinitrix::Api(api) => match api {
Api::Exit => { Api::exit => {
send_status_output!("Terminating the application.."); send_status_output!("Terminating the application..");
EventStatus::Terminate EventStatus::Terminate
} }
Api::RoomMessageSend(msg) => { Api::room_message_send { message } => {
if let Some(room) = app.status.room_mut() { if let Some(room) = app.status.room_mut() {
room.send(msg.clone()).await?; room.send(message.clone()).await?;
send_status_output!("Sent message: `{}`", msg); send_status_output!("Sent message: `{}`", message);
} else { } else {
// FIXME(@soispha): This should raise an error within lua, as it would // FIXME(@soispha): This should raise an error within lua, as it would
// otherwise be very confusing <2023-09-20> // otherwise be very confusing <2023-09-20>
warn!("Can't send message: `{}`, as there is no open room!", &msg); warn!(
"Can't send message: `{}`, as there is no open room!",
&message
);
} }
// NOTE(@soispha): This is temporary, until trixy can do it automatically <2024-05-03>
mem::forget(message);
EventStatus::Ok EventStatus::Ok
} }
Api::Help(_) => todo!(),
Api::Ui(ui) => match ui { Api::Ui(ui) => match ui {
Ui::CommandLineShow => { Ui::set_mode { mode } => match mode {
app.ui.cli_enable(); Mode::Normal => {
app.status.set_state(State::Command);
send_status_output!("CLI online");
EventStatus::Ok
}
Ui::CommandLineHide => {
app.ui.cli_disable();
send_status_output!("CLI offline");
EventStatus::Ok
}
Ui::CyclePlanes => {
app.ui.cycle_main_input_position();
send_status_output!("Switched main input position");
EventStatus::Ok
}
Ui::CyclePlanesRev => {
app.ui.cycle_main_input_position_rev();
send_status_output!("Switched main input position; reversed");
EventStatus::Ok
}
Ui::SetModeNormal => {
app.status.set_state(State::Normal); app.status.set_state(State::Normal);
send_status_output!("Set input mode to Normal"); send_status_output!("Set input mode to Normal");
EventStatus::Ok EventStatus::Ok
} }
Ui::SetModeInsert => { Mode::Insert => {
app.status.set_state(State::Insert); app.status.set_state(State::Insert);
app.ui.set_input_position(InputPosition::MessageCompose); app.ui.set_input_position(InputPosition::MessageCompose);
send_status_output!("Set input mode to Insert"); send_status_output!("Set input mode to Insert");
EventStatus::Ok EventStatus::Ok
} }
Mode::Command => {
app.ui.cli_enable();
app.status.set_state(State::Command);
send_status_output!("Set input mode to CLI");
EventStatus::Ok
}
},
Ui::cycle_planes => {
app.ui.cycle_main_input_position();
send_status_output!("Switched main input position");
EventStatus::Ok
}
Ui::cycle_planes_rev => {
app.ui.cycle_main_input_position_rev();
send_status_output!("Switched main input position; reversed");
EventStatus::Ok
}
}, },
Api::Keymaps(keymaps) => match keymaps { Api::Keymaps(keymaps) => match keymaps {
Keymaps::Add((mode, key, callback)) => { Keymaps::add {
mode,
key,
callback,
} => {
mode.chars().for_each(|char| { mode.chars().for_each(|char| {
info!("Setting keymaping ('{}') for mode '{}'", key, char); info!("Setting keymaping ('{}') for mode '{}'", key, char);
let parsed_keys = key let parsed_keys = key
@ -179,7 +164,7 @@ pub async fn handle(
} }
trie.insert(&parsed_keys, callback.to_owned()) trie.insert(&parsed_keys, callback.to_owned())
.map_err(|err| { .map_err(|err| {
send_error_output!(err.to_string()); send_error_output!(format!("{:#?}", err));
}) })
.expect("We already dealt with the error") .expect("We already dealt with the error")
} }
@ -187,31 +172,33 @@ pub async fn handle(
}; };
}); });
mem::forget(key);
mem::forget(mode);
EventStatus::Ok EventStatus::Ok
} }
// TODO(@soispha): Well.., we should probably add these functions: <2023-10-15> // FIXME(@soispha): It would be nice to have these functions, but well..
Keymaps::Remove((mode, key)) => todo!(), // someone needs to write them <2024-05-03>
Keymaps::Get(mode) => todo!(), Keymaps::remove { mode, key } => todo!(),
Keymaps::get { mode } => todo!(),
}, },
Api::Raw(raw) => match raw { Api::Raw(raw) => match raw {
Raw::RaiseError(err) => { Raw::raise_error { error_message } => {
send_error_output!(err); send_error_output!(error_message);
mem::forget(error_message);
EventStatus::Ok EventStatus::Ok
} }
Raw::DisplayOutput(output) => { Raw::display_output { output_message } => {
// 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_message);
mem::forget(output_message);
EventStatus::Ok EventStatus::Ok
} }
Raw::Private(_) => { Raw::send_input_unprocessed { input } => {
// no-op, read the comment about it in the `command_list` let output = match app.status.state() {
EventStatus::Ok
}
Raw::SendInputUnprocessed(char) => match app.status.state() {
State::Insert => { State::Insert => {
let key = Key::from_str(char)?; let key = Key::from_str(&input)?;
let cross_input: Event = key.try_into()?; let cross_input: Event = key.try_into()?;
app.ui app.ui
.message_compose .message_compose
@ -219,7 +206,7 @@ pub async fn handle(
EventStatus::Ok EventStatus::Ok
} }
State::Command => { State::Command => {
let key = Key::from_str(char)?; let key = Key::from_str(&input)?;
let cross_input: Event = key.try_into()?; let cross_input: Event = key.try_into()?;
app.ui app.ui
.cli .cli
@ -234,13 +221,91 @@ pub async fn handle(
old_state: _, old_state: _,
pending_keys: _, pending_keys: _,
} => EventStatus::Ok, } => EventStatus::Ok,
}, };
}, mem::forget(input);
}, output
Trinitrix::Std(_) => { }
// no-op, read the comment about it in the `command_list` Raw::Private(private) => {
// no-op, this was used to store functions (not so sure, if we need it
// any longer)
EventStatus::Ok EventStatus::Ok
} }
}, },
},
},
// Command::Print(output) => {
// let output_str: String = output.to_string();
// send_status_output!(output_str);
// EventStatus::Ok
// }
//
// Command::Trinitrix(trinitrix) => match trinitrix {
// Trinitrix::Debug(debug) => match debug {
// Debug::Greet(msg) => {
// send_main_output!("Greeting, {}!", msg);
// EventStatus::Ok
// }
// Debug::GreetMultiple => {
// let mut table: Table = HashMap::new();
// table.insert("UserId".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);
// EventStatus::Ok
// }
// },
// Trinitrix::Api(api) => match api {
// Api::RoomMessageSend(msg) => {
// }
// Api::Help(_) => todo!(),
// Api::Ui(ui) => match ui {
// Ui::CommandLineShow => {
// }
// Ui::CommandLineHide => {
// app.ui.cli_disable();
// send_status_output!("CLI offline");
// EventStatus::Ok
// }
// Ui::CyclePlanes => {
// }
// Ui::CyclePlanesRev => {
// }
// Ui::SetModeNormal => {
// }
// Ui::SetModeInsert => {
// }
// },
// Api::Keymaps(keymaps) => match keymaps {
// Keymaps::Add((mode, key, callback)) => {
// }
// // TODO(@soispha): Well.., we should probably add these functions: <2023-10-15>
// Keymaps::Remove((mode, key)) => todo!(),
// Keymaps::Get(mode) => todo!(),
// },
// Api::Raw(raw) => match raw {
// Raw::RaiseError(err) => {
// }
// Raw::DisplayOutput(output) => {
// }
// Raw::Private(_) => {
// }
// Raw::SendInputUnprocessed(char) =>
// },
// },
// Trinitrix::Std(_) => {
// // no-op, read the comment about it in the `command_list`
// EventStatus::Ok
// }
// },
}) })
} }

View File

@ -1,13 +1,13 @@
use anyhow::Result; // use anyhow::Result;
//
use crate::app::{ // use crate::app::{
command_interface::command_transfer_value::support_types::Function, events::EventStatus, App, // command_interface::command_transfer_value::support_types::Function, events::EventStatus, App,
}; // };
//
// TODO(@soispha): We just assume for now that all functions originate in lua. This module will in // // TODO(@soispha): We just assume for now that all functions originate in lua. This module will in
// future versions house check for the language the function came from <2023-10-15> // // future versions house check for the language the function came from <2023-10-15>
pub async fn handle(app: &mut App<'_>, function: Function) -> Result<EventStatus> { // pub async fn handle(app: &mut App<'_>, function: Function) -> Result<EventStatus> {
app.lua.execute_function(function).await; // app.lua.execute_function(function).await;
//
Ok(EventStatus::Ok) // Ok(EventStatus::Ok)
} // }

View File

@ -4,7 +4,13 @@ use crossterm::event::Event as CrosstermEvent;
use keymaps::key_repr::{Key, Keys}; use keymaps::key_repr::{Key, Keys};
use crate::app::{ use crate::app::{
command_interface::{Api::Raw, Command, Raw::SendInputUnprocessed, Trinitrix::Api}, command_interface::{
trinitrix::{
api::{raw::Raw, Api},
Trinitrix,
},
Commands,
},
events::{Event, EventStatus}, events::{Event, EventStatus},
status::State, status::State,
App, App,
@ -24,7 +30,11 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
for key in pending_keys { for key in pending_keys {
app.tx app.tx
.send(Event::CommandEvent( .send(Event::CommandEvent(
Command::Trinitrix(Api(Raw(SendInputUnprocessed(key.to_string_repr())))), Commands::Trinitrix(Trinitrix::Api(Api::Raw(
Raw::send_input_unprocessed {
input: key.to_string_repr(),
},
))),
None, None,
)) ))
.await?; .await?;
@ -34,9 +44,9 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
// Just let the input event slip through if no keymap matches // Just let the input event slip through if no keymap matches
app.tx app.tx
.send(Event::CommandEvent( .send(Event::CommandEvent(
Command::Trinitrix(Api(Raw(SendInputUnprocessed( Commands::Trinitrix(Trinitrix::Api(Api::Raw(Raw::send_input_unprocessed {
converted_key.to_string_repr(), input: converted_key.to_string_repr(),
)))), }))),
None, None,
)) ))
.await?; .await?;
@ -88,7 +98,9 @@ pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<E
let function = possible_key_map let function = possible_key_map
.value() .value()
.expect("This node is terminal and a child, it should have a value"); .expect("This node is terminal and a child, it should have a value");
app.tx.send(Event::Function(*function)).await?;
function();
// app.tx.send(Event::Function(*function)).await?;
app.status.set_state(old_state.to_owned()); app.status.set_state(old_state.to_owned());
} else { } else {
// The choice does not have a value attached to it (might be a waypoint) // The choice does not have a value attached to it (might be a waypoint)

View File

@ -1,14 +1,14 @@
use anyhow::Result; // use anyhow::Result;
use cli_log::trace; // use cli_log::trace;
//
use crate::app::{events::EventStatus, App}; // use crate::app::{events::EventStatus, App};
//
// This function is here mainly to reserve this spot for further processing of the lua command. // // This function is here mainly to reserve this spot for further processing of the lua command.
// TODO(@Soispha): Move the lua executor thread code from app to this module // // TODO(@Soispha): Move the lua executor thread code from app to this module
pub async fn handle(app: &mut App<'_>, command: String) -> Result<EventStatus> { // pub async fn handle(app: &mut App<'_>, command: String) -> Result<EventStatus> {
trace!("Recieved ci command: `{command}`; executing.."); // trace!("Recieved ci command: `{command}`; executing..");
//
app.lua.execute_code(command).await; // app.lua.execute_code(command).await;
//
Ok(EventStatus::Ok) // Ok(EventStatus::Ok)
} // }

View File

@ -8,4 +8,4 @@ pub mod matrix;
// ci // ci
pub mod command; pub mod command;
pub mod lua_command; pub mod lua_command;
pub mod function; // pub mod function;

View File

@ -3,26 +3,18 @@ pub mod listeners;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use crate::app::{ use crate::app::{command_interface::Commands, status::State, App};
command_interface::{
command_transfer_value::{support_types::Function, CommandTransferValue},
Command,
},
status::State,
App,
};
use cli_log::trace; use cli_log::trace;
use crossterm::event::Event as CrosstermEvent; use crossterm::event::Event as CrosstermEvent;
use handlers::{command, function, input, lua_command, matrix, setup}; use handlers::{command, input, lua_command, matrix, setup};
use tokio::sync::oneshot;
#[derive(Debug)] #[derive(Debug)]
pub enum Event { pub enum Event {
InputEvent(CrosstermEvent), InputEvent(CrosstermEvent),
MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse), MatrixEvent(matrix_sdk::deserialized_responses::SyncResponse),
CommandEvent(Command, Option<oneshot::Sender<CommandTransferValue>>), // FIXME(@soispha): The `String` is also wrong <2024-05-03>
CommandEvent(Commands, Option<trixy::oneshot::Sender<String>>),
LuaCommand(String), LuaCommand(String),
Function(Function),
} }
impl Event { impl Event {
@ -37,13 +29,13 @@ impl Event {
.await .await
.with_context(|| format!("Failed to handle command event: `{:#?}`", event)), .with_context(|| format!("Failed to handle command event: `{:#?}`", event)),
Event::LuaCommand(lua_code) => lua_command::handle(app, lua_code.to_owned()) Event::LuaCommand(lua_code) => Ok(EventStatus::Terminate),
.await // lua_command::handle(app, lua_code.to_owned())
.with_context(|| format!("Failed to handle lua code: `{}`", lua_code)), // .await
Event::Function(function) => function::handle(app, function.to_owned()) // .with_context(|| format!("Failed to handle lua code: `{}`", lua_code)),
.await // Event::Function(function) => function::handle(app, function.to_owned())
.with_context(|| format!("Failed to handle function: `{}`", function)), // .await
// .with_context(|| format!("Failed to handle function: `{}`", function)),
Event::InputEvent(event) => match app.status.state() { Event::InputEvent(event) => match app.status.state() {
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

@ -6,6 +6,7 @@ pub mod status;
use std::{ use std::{
collections::HashMap, collections::HashMap,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::OnceLock,
}; };
use anyhow::{Context, Error, Result}; use anyhow::{Context, Error, Result};
@ -13,12 +14,12 @@ use cli_log::{info, warn};
use directories::ProjectDirs; use directories::ProjectDirs;
use keymaps::trie::Node; use keymaps::trie::Node;
use matrix_sdk::Client; use matrix_sdk::Client;
use tokio::sync::mpsc; use tokio::sync::mpsc::{self, Sender};
use tokio_util::sync::CancellationToken; use tokio_util::sync::CancellationToken;
use self::command_interface::{ // use self::command_interface::{
command_transfer_value::support_types::Function, lua_command_manager::LuaCommandManager, // lua_command_manager::LuaCommandManager,
}; // };
use crate::{ use crate::{
accounts::{Account, AccountsManager}, accounts::{Account, AccountsManager},
@ -40,13 +41,14 @@ pub struct App<'runtime> {
input_listener_killer: CancellationToken, input_listener_killer: CancellationToken,
matrix_listener_killer: CancellationToken, matrix_listener_killer: CancellationToken,
lua: LuaCommandManager, // lua: LuaCommandManager,
project_dirs: ProjectDirs, project_dirs: ProjectDirs,
key_mappings: HashMap<State, Node<Function>>, key_mappings: HashMap<State, Node<extern "C" fn()>>,
} }
pub static COMMAND_TRANSMITTER: OnceLock<Sender<Event>> = OnceLock::new();
impl App<'_> { impl App<'_> {
pub fn new() -> Result<Self> { pub fn new() -> Result<Self> {
let path: &std::path::Path = Path::new("userdata/accounts.json"); let path: &std::path::Path = Path::new("userdata/accounts.json");
@ -58,6 +60,11 @@ impl App<'_> {
}; };
let (tx, rx) = mpsc::channel(256); let (tx, rx) = mpsc::channel(256);
COMMAND_TRANSMITTER
.set(tx.clone())
.expect("The cell should always be empty at this point");
Ok(Self { Ok(Self {
ui: central::UI::new()?, ui: central::UI::new()?,
accounts_manager: AccountsManager::new(config)?, accounts_manager: AccountsManager::new(config)?,
@ -68,7 +75,7 @@ impl App<'_> {
input_listener_killer: CancellationToken::new(), input_listener_killer: CancellationToken::new(),
matrix_listener_killer: CancellationToken::new(), matrix_listener_killer: CancellationToken::new(),
lua: LuaCommandManager::new(tx), // lua: LuaCommandManager::new(tx),
// TODO: We probably want to populate the strings below a bit more <2023-09-09> // TODO: We probably want to populate the strings below a bit more <2023-09-09>
project_dirs: ProjectDirs::from("", "", "trinitrix").context( project_dirs: ProjectDirs::from("", "", "trinitrix").context(

4
src/bin/api.rs Normal file
View File

@ -0,0 +1,4 @@
fn main() {
let output = include_str!(concat!(env!("OUT_DIR"), "/api.rs"));
println!("{}", output);
}

View File

@ -2,22 +2,22 @@ use std::path::PathBuf;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
// TODO: The description could be better // TODO: The description is wrong
/// A terminal client for the matrix chat protocol /// A terminal client for the matrix chat protocol
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
pub struct Args { pub struct Args {
#[command(subcommand)] #[command(subcommand)]
/// The subcommand to execute, default is start /// The subcommand to execute, default is start
pub subcommand: Option<Command>, pub subcommand: Option<Command>,
#[clap(value_parser, long, short)] #[arg(long, short)]
/// Path to the Lua config file, executed instead of the normal one /// Path to the Lua config file, executed instead of the normal one
pub lua_config_file: Option<PathBuf>, pub lua_config_file: Option<PathBuf>,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
pub enum Command { pub enum Command {
#[clap(value_parser)]
/// Starts the main TUI client /// Starts the main TUI client
Start {}, Start {},
} }

View File

@ -22,3 +22,8 @@ async fn main() -> anyhow::Result<()> {
Ok(()) Ok(())
} }
// FIXME(@soispha): Re-exports for trixy, this should be configurable <2024-05-03>
pub use crate::app::command_interface::handle_cmd;
pub use crate::app::command_interface::Commands;
pub use crate::app::command_interface::*;