forked from trinitrix/core
feat(command_interface): Add support for namespaces
This commit is contained in:
parent
29aa6c1d33
commit
27d00c564c
|
@ -1,50 +1,96 @@
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote, ToTokens};
|
||||||
use syn::{DeriveInput, Field, Type};
|
use syn::{punctuated::Punctuated, Token, Type, Ident};
|
||||||
|
|
||||||
use super::{get_input_type_of_bare_fn_field, parse_derive_input_as_named_fields};
|
use crate::{DataCommandEnum, Field};
|
||||||
|
|
||||||
pub fn command_enum(input: &DeriveInput) -> TokenStream2 {
|
use super::get_input_type_of_bare_fn_field;
|
||||||
let named_fields = parse_derive_input_as_named_fields(input);
|
|
||||||
let fields: TokenStream2 = named_fields
|
pub fn command_enum(input: &DataCommandEnum) -> TokenStream2 {
|
||||||
.named
|
let (fields, namespace_enums): (TokenStream2, TokenStream2) =
|
||||||
.iter()
|
turn_fields_to_enum(&input.fields);
|
||||||
.map(|field| turn_struct_fieled_to_enum(field))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
#fields
|
#fields
|
||||||
}
|
}
|
||||||
|
#namespace_enums
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn turn_struct_fieled_to_enum(field: &Field) -> TokenStream2 {
|
fn turn_fields_to_enum(fields: &Punctuated<Field, Token![,]>) -> (TokenStream2, TokenStream2) {
|
||||||
|
let output: Vec<_> = fields
|
||||||
|
.iter()
|
||||||
|
.map(|field| turn_struct_field_to_enum(field))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut fields_output: TokenStream2 = Default::default();
|
||||||
|
let mut namespace_enums_output: TokenStream2 = Default::default();
|
||||||
|
|
||||||
|
for (fields, namespace_enum) in output {
|
||||||
|
fields_output.extend(fields.to_token_stream());
|
||||||
|
namespace_enums_output.extend(namespace_enum.to_token_stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
(fields_output, namespace_enums_output)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn turn_struct_field_to_enum(field: &Field) -> (TokenStream2, TokenStream2) {
|
||||||
|
match field {
|
||||||
|
Field::Function(fun_field) => {
|
||||||
let field_name = format_ident!(
|
let field_name = format_ident!(
|
||||||
"{}",
|
"{}",
|
||||||
field
|
fun_field
|
||||||
.ident
|
.name
|
||||||
.as_ref()
|
|
||||||
.expect("These are named fields, it should be Some(<name>)")
|
|
||||||
.to_string()
|
.to_string()
|
||||||
.from_case(Case::Snake)
|
.from_case(Case::Snake)
|
||||||
.to_case(Case::Pascal)
|
.to_case(Case::Pascal)
|
||||||
);
|
);
|
||||||
|
|
||||||
let input_type: Option<Type> = get_input_type_of_bare_fn_field(field);
|
let input_type: Option<Type> = get_input_type_of_bare_fn_field(fun_field);
|
||||||
|
|
||||||
match input_type {
|
match input_type {
|
||||||
Some(input_type) => {
|
Some(input_type) => (
|
||||||
quote! {
|
quote! {
|
||||||
#field_name(#input_type),
|
#field_name(#input_type),
|
||||||
}
|
},
|
||||||
}
|
quote! {},
|
||||||
None => {
|
),
|
||||||
|
None => (
|
||||||
quote! {
|
quote! {
|
||||||
#field_name,
|
#field_name,
|
||||||
}
|
},
|
||||||
|
quote! {},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Field::Namespace(namespace) => {
|
||||||
|
let (namespace_output_fields, namespace_output_namespace_enums) =
|
||||||
|
turn_fields_to_enum(&namespace.fields);
|
||||||
|
let namespace_name: Ident = format_ident!(
|
||||||
|
"{}",
|
||||||
|
namespace.path.iter().map(|name| name.to_string()).collect::<String>()
|
||||||
|
);
|
||||||
|
|
||||||
|
let new_namespace_name: Ident = format_ident!(
|
||||||
|
"{}",
|
||||||
|
namespace_name.to_string().from_case(Case::Snake).to_case(Case::Pascal)
|
||||||
|
);
|
||||||
|
|
||||||
|
(
|
||||||
|
quote! {
|
||||||
|
#new_namespace_name(#new_namespace_name),
|
||||||
|
},
|
||||||
|
quote! {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum #new_namespace_name {
|
||||||
|
#namespace_output_fields
|
||||||
|
}
|
||||||
|
#namespace_output_namespace_enums
|
||||||
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,35 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{DeriveInput, Field};
|
use syn::{punctuated::Punctuated, Token};
|
||||||
|
|
||||||
use crate::generate::parse_derive_input_as_named_fields;
|
use crate::{DataCommandEnum, Field, FunctionDeclaration, NamespacePath};
|
||||||
|
|
||||||
pub fn generate_add_lua_functions_to_globals(input: &DeriveInput) -> TokenStream2 {
|
pub fn generate_add_lua_functions_to_globals(input: &DataCommandEnum) -> TokenStream2 {
|
||||||
let named_fields = parse_derive_input_as_named_fields(input);
|
fn turn_field_to_functions(
|
||||||
let function_adders: TokenStream2 = named_fields
|
input: &Punctuated<Field, Token![,]>,
|
||||||
.named
|
namespace_path: Option<&NamespacePath>,
|
||||||
|
) -> TokenStream2 {
|
||||||
|
input
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| generate_function_adder(field))
|
.map(|field| match field {
|
||||||
.collect();
|
crate::Field::Function(function) => {
|
||||||
|
generate_function_adder(function, namespace_path)
|
||||||
|
}
|
||||||
|
crate::Field::Namespace(namespace) => {
|
||||||
|
let mut passed_namespace =
|
||||||
|
namespace_path.unwrap_or(&Default::default()).clone();
|
||||||
|
namespace
|
||||||
|
.path
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|val| passed_namespace.push(val));
|
||||||
|
|
||||||
|
turn_field_to_functions(&namespace.fields, Some(&passed_namespace))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
let function_adders: TokenStream2 = turn_field_to_functions(&input.fields, None);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
pub fn add_lua_functions_to_globals(
|
pub fn add_lua_functions_to_globals(
|
||||||
|
@ -28,15 +47,67 @@ pub fn generate_add_lua_functions_to_globals(input: &DeriveInput) -> TokenStream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_function_adder(field: &Field) -> TokenStream2 {
|
fn generate_function_adder(
|
||||||
let field_ident = field
|
field: &FunctionDeclaration,
|
||||||
.ident
|
namespace_path: Option<&NamespacePath>,
|
||||||
.as_ref()
|
) -> TokenStream2 {
|
||||||
.expect("This is should be a named field");
|
let field_ident = &field.name;
|
||||||
|
|
||||||
let function_ident = format_ident!("wrapped_lua_function_{}", field_ident);
|
let function_ident = format_ident!("wrapped_lua_function_{}", field_ident);
|
||||||
let function_name = field_ident.to_string();
|
let function_name = field_ident.to_string();
|
||||||
|
|
||||||
|
let setter = if let Some(namespace_path) = namespace_path {
|
||||||
|
// ```lua
|
||||||
|
// local globals = {
|
||||||
|
// ns1: {
|
||||||
|
// ns_value,
|
||||||
|
// ns_value2,
|
||||||
|
// },
|
||||||
|
// ns2: {
|
||||||
|
// ns_value3,
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ns1.ns_value
|
||||||
|
// ```
|
||||||
|
let mut counter = 0;
|
||||||
|
let namespace_table_gen: TokenStream2 = namespace_path.iter().map(|path| {
|
||||||
|
let path = path.to_string();
|
||||||
|
counter += 1;
|
||||||
|
let mut set_function: TokenStream2 = Default::default();
|
||||||
|
if counter == namespace_path.len() {
|
||||||
|
set_function = quote! {
|
||||||
|
table.set(#function_name, #function_ident).expect(
|
||||||
|
"Setting a static global value should work"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
quote! {
|
||||||
|
let table: mlua::Table = {
|
||||||
|
if table.contains_key(#path).expect("This check should work") {
|
||||||
|
let table2 = table.get(#path).expect("This was already checked");
|
||||||
|
table2
|
||||||
|
} else {
|
||||||
|
table.set(#path, lua.create_table().expect("This should also always work")).expect("Setting this value should work");
|
||||||
|
table.get(#path).expect("This was set, just above")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#set_function
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
let table = &globals;
|
||||||
|
{
|
||||||
|
#namespace_table_gen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
globals.set(#function_name, #function_ident).expect(
|
||||||
|
"Setting a static global value should work"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
quote! {
|
quote! {
|
||||||
{
|
{
|
||||||
let #function_ident = lua.create_async_function(#field_ident).expect(
|
let #function_ident = lua.create_async_function(#field_ident).expect(
|
||||||
|
@ -45,10 +116,7 @@ fn generate_function_adder(field: &Field) -> TokenStream2 {
|
||||||
#function_name
|
#function_name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
#setter
|
||||||
globals.set(#function_name, #function_ident).expect(
|
|
||||||
"Setting a static global value should work"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,20 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::DeriveInput;
|
|
||||||
|
|
||||||
use crate::generate::lua_wrapper::{
|
use crate::{
|
||||||
|
generate::lua_wrapper::{
|
||||||
lua_functions_to_globals::generate_add_lua_functions_to_globals,
|
lua_functions_to_globals::generate_add_lua_functions_to_globals,
|
||||||
rust_wrapper_functions::generate_rust_wrapper_functions,
|
rust_wrapper_functions::generate_rust_wrapper_functions,
|
||||||
|
},
|
||||||
|
DataCommandEnum,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod lua_functions_to_globals;
|
mod lua_functions_to_globals;
|
||||||
mod rust_wrapper_functions;
|
mod rust_wrapper_functions;
|
||||||
|
|
||||||
pub fn lua_wrapper(input: &DeriveInput) -> TokenStream2 {
|
pub fn lua_wrapper(input: &DataCommandEnum) -> TokenStream2 {
|
||||||
let add_lua_functions_to_globals = generate_add_lua_functions_to_globals(input);
|
let add_lua_functions_to_globals = generate_add_lua_functions_to_globals(input);
|
||||||
let rust_wrapper_functions = generate_rust_wrapper_functions(input);
|
let rust_wrapper_functions = generate_rust_wrapper_functions(None, input);
|
||||||
quote! {
|
quote! {
|
||||||
#add_lua_functions_to_globals
|
#add_lua_functions_to_globals
|
||||||
#rust_wrapper_functions
|
#rust_wrapper_functions
|
||||||
|
|
|
@ -1,21 +1,39 @@
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{format_ident, quote};
|
use quote::quote;
|
||||||
use syn::{
|
use syn::{punctuated::Punctuated, token::Comma, GenericArgument, Lifetime, Token, Type};
|
||||||
punctuated::Punctuated, token::Comma, DeriveInput, Field, GenericArgument, Lifetime, Type,
|
|
||||||
|
use crate::{
|
||||||
|
generate::{get_input_type_of_bare_fn_field, get_return_type_of_bare_fn_field},
|
||||||
|
DataCommandEnum, Field, FunctionDeclaration, NamespacePath,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::generate::{
|
pub fn generate_rust_wrapper_functions(
|
||||||
get_input_type_of_bare_fn_field, get_return_type_of_bare_fn_field,
|
namespace: Option<&NamespacePath>,
|
||||||
parse_derive_input_as_named_fields,
|
input: &DataCommandEnum,
|
||||||
};
|
) -> TokenStream2 {
|
||||||
|
generate_rust_wrapper_functions_rec(namespace, &input.fields)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn generate_rust_wrapper_functions(input: &DeriveInput) -> TokenStream2 {
|
pub fn generate_rust_wrapper_functions_rec(
|
||||||
let named_fields = parse_derive_input_as_named_fields(input);
|
namespace: Option<&NamespacePath>,
|
||||||
let wrapped_functions: TokenStream2 = named_fields
|
input: &Punctuated<Field, Token![,]>,
|
||||||
.named
|
) -> TokenStream2 {
|
||||||
|
let wrapped_functions: TokenStream2 = input
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| wrap_lua_function(field))
|
.map(|field| match field {
|
||||||
|
Field::Function(fun_field) => {
|
||||||
|
wrap_lua_function(namespace.unwrap_or(&Default::default()), fun_field)
|
||||||
|
}
|
||||||
|
Field::Namespace(nasp) => {
|
||||||
|
let mut passed_namespace = namespace.unwrap_or(&Default::default()).clone();
|
||||||
|
nasp.path
|
||||||
|
.clone()
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|val| passed_namespace.push(val));
|
||||||
|
generate_rust_wrapper_functions_rec(Some(&passed_namespace), &nasp.fields)
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -23,12 +41,12 @@ pub fn generate_rust_wrapper_functions(input: &DeriveInput) -> TokenStream2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_lua_function(field: &Field) -> TokenStream2 {
|
fn wrap_lua_function(namespace: &NamespacePath, field: &FunctionDeclaration) -> TokenStream2 {
|
||||||
let input_type = get_input_type_of_bare_fn_field(field);
|
let input_type = get_input_type_of_bare_fn_field(field);
|
||||||
let return_type = get_return_type_of_bare_fn_field(field);
|
let return_type = get_return_type_of_bare_fn_field(field);
|
||||||
|
|
||||||
let function_name = field.ident.as_ref().expect("This should be a named field");
|
let function_name = &field.name;
|
||||||
let function_body = get_function_body(field, input_type.is_some(), &return_type);
|
let function_body = get_function_body(&namespace, field, input_type.is_some(), &return_type);
|
||||||
|
|
||||||
let lifetime_args =
|
let lifetime_args =
|
||||||
get_and_add_lifetimes_form_inputs_and_outputs(input_type.clone(), return_type);
|
get_and_add_lifetimes_form_inputs_and_outputs(input_type.clone(), return_type);
|
||||||
|
@ -98,32 +116,62 @@ fn get_and_add_lifetimes_form_inputs_and_outputs<'a>(
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_function_body(field: &Field, has_input: bool, output_type: &Option<Type>) -> TokenStream2 {
|
fn get_function_body(
|
||||||
|
namespace: &NamespacePath,
|
||||||
|
field: &FunctionDeclaration,
|
||||||
|
has_input: bool,
|
||||||
|
output_type: &Option<Type>,
|
||||||
|
) -> TokenStream2 {
|
||||||
let command_name = field
|
let command_name = field
|
||||||
.ident
|
.name
|
||||||
.as_ref()
|
|
||||||
.expect("These are named fields, it should be Some(<name>)")
|
|
||||||
.to_string()
|
.to_string()
|
||||||
.from_case(Case::Snake)
|
.from_case(Case::Snake)
|
||||||
.to_case(Case::Pascal);
|
.to_case(Case::Pascal);
|
||||||
let command_ident = format_ident!("{}", command_name);
|
|
||||||
|
|
||||||
let send_output = if has_input {
|
let command_ident = {
|
||||||
quote! {
|
if has_input {
|
||||||
Event::CommandEvent(
|
format!("{}(", command_name)
|
||||||
Command::#command_ident(input.clone()),
|
|
||||||
Some(callback_tx),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
quote! {
|
command_name.clone()
|
||||||
Event::CommandEvent(
|
|
||||||
Command::#command_ident,
|
|
||||||
Some(callback_tx),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let command_namespace: String = {
|
||||||
|
namespace
|
||||||
|
.iter()
|
||||||
|
.map(|path| {
|
||||||
|
let path_enum_name: String = path
|
||||||
|
.to_string()
|
||||||
|
.from_case(Case::Snake)
|
||||||
|
.to_case(Case::Pascal);
|
||||||
|
|
||||||
|
path_enum_name.clone() + "(" + &path_enum_name + "::"
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("")
|
||||||
|
};
|
||||||
|
|
||||||
|
let send_output: TokenStream2 = {
|
||||||
|
let finishing_brackets = {
|
||||||
|
if has_input {
|
||||||
|
let mut output = "input.clone()".to_owned();
|
||||||
|
output.push_str(&(0..namespace.len()).map(|_| ')').collect::<String>());
|
||||||
|
output
|
||||||
|
} else {
|
||||||
|
(0..namespace.len()).map(|_| ')').collect::<String>()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
("Event::CommandEvent( Command::".to_owned()
|
||||||
|
+ &command_namespace
|
||||||
|
+ &command_ident
|
||||||
|
+ &finishing_brackets
|
||||||
|
+ {if has_input {")"} else {""}} /* Needed as command_name opens one */
|
||||||
|
+ ",Some(callback_tx))")
|
||||||
|
.parse()
|
||||||
|
.expect("This code should be valid")
|
||||||
|
};
|
||||||
|
|
||||||
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"));
|
return Ok(output.into_lua(lua).expect("This conversion should always work"));
|
||||||
|
|
|
@ -3,18 +3,9 @@ mod lua_wrapper;
|
||||||
|
|
||||||
pub use command_enum::command_enum;
|
pub use command_enum::command_enum;
|
||||||
pub use lua_wrapper::lua_wrapper;
|
pub use lua_wrapper::lua_wrapper;
|
||||||
use syn::{DeriveInput, Field, FieldsNamed, ReturnType, Type, TypeBareFn};
|
use syn::{ReturnType, Type, TypeBareFn};
|
||||||
|
|
||||||
pub fn parse_derive_input_as_named_fields(input: &DeriveInput) -> FieldsNamed {
|
use crate::FunctionDeclaration;
|
||||||
match &input.data {
|
|
||||||
syn::Data::Struct(input) => match &input.fields {
|
|
||||||
syn::Fields::Named(named_fields) => named_fields,
|
|
||||||
_ => unimplemented!("The macro only works for named fields (e.g.: `Name: Type`)"),
|
|
||||||
},
|
|
||||||
_ => unimplemented!("The macro only works for structs"),
|
|
||||||
}
|
|
||||||
.to_owned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bare_fn_input_type(function: &TypeBareFn) -> Option<Type> {
|
pub fn get_bare_fn_input_type(function: &TypeBareFn) -> Option<Type> {
|
||||||
if function.inputs.len() == 1 {
|
if function.inputs.len() == 1 {
|
||||||
|
@ -37,7 +28,7 @@ pub fn get_bare_fn_input_type(function: &TypeBareFn) -> Option<Type> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_input_type_of_bare_fn_field(field: &Field) -> Option<Type> {
|
pub fn get_input_type_of_bare_fn_field(field: &FunctionDeclaration) -> Option<Type> {
|
||||||
match &field.ty {
|
match &field.ty {
|
||||||
syn::Type::BareFn(function) => get_bare_fn_input_type(&function),
|
syn::Type::BareFn(function) => get_bare_fn_input_type(&function),
|
||||||
_ => unimplemented!(
|
_ => unimplemented!(
|
||||||
|
@ -46,7 +37,7 @@ pub fn get_input_type_of_bare_fn_field(field: &Field) -> Option<Type> {
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_return_type_of_bare_fn_field(field: &Field) -> Option<Type> {
|
pub fn get_return_type_of_bare_fn_field(field: &FunctionDeclaration) -> Option<Type> {
|
||||||
match &field.ty {
|
match &field.ty {
|
||||||
syn::Type::BareFn(function) => get_bare_fn_return_type(&function),
|
syn::Type::BareFn(function) => get_bare_fn_return_type(&function),
|
||||||
_ => unimplemented!(
|
_ => unimplemented!(
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::DeriveInput;
|
use syn::{
|
||||||
|
braced, parse::Parse, parse_macro_input, punctuated::Punctuated, token, Attribute, Ident,
|
||||||
|
Token, Type,
|
||||||
|
};
|
||||||
|
|
||||||
mod generate;
|
mod generate;
|
||||||
|
|
||||||
|
@ -14,19 +17,17 @@ mod generate;
|
||||||
/// the rust wrapper functions to the lua globals.
|
/// the rust wrapper functions to the lua globals.
|
||||||
///
|
///
|
||||||
/// The input and output values of the wrapped functions are derived from the values specified in
|
/// The input and output values of the wrapped functions are derived from the values specified in
|
||||||
/// the `Commands` struct.
|
/// the input to the `parse_command_enum` proc macro.
|
||||||
/// The returned values will be returned directly to the lua context, this allows to nest functions.
|
|
||||||
///
|
///
|
||||||
/// For example this rust code:
|
/// For example this rust code:
|
||||||
/// ```rust
|
/// ```no_run
|
||||||
/// #[ci_command_enum]
|
/// parse_command_enum! {
|
||||||
/// struct Commands {
|
|
||||||
/// /// Greets the user
|
/// /// Greets the user
|
||||||
/// greet: fn(String) -> String,
|
/// greet: fn(String) -> String,
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
/// results in this expanded code:
|
/// results in this expanded code:
|
||||||
/// ```rust
|
/// ```no_run
|
||||||
/// #[derive(Debug)]
|
/// #[derive(Debug)]
|
||||||
/// pub enum Command {
|
/// pub enum Command {
|
||||||
/// Greet(String),
|
/// Greet(String),
|
||||||
|
@ -72,12 +73,118 @@ mod generate;
|
||||||
/// };
|
/// };
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[proc_macro_attribute]
|
#[derive(Debug)]
|
||||||
pub fn ci_command_enum(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
struct DataCommandEnum {
|
||||||
// Construct a representation of Rust code as a syntax tree
|
#[allow(dead_code)]
|
||||||
// that we can manipulate
|
commands_token: kw::commands,
|
||||||
let input: DeriveInput = syn::parse(input)
|
|
||||||
.expect("This should always be valid rust code, as it's extracted from direct code");
|
#[allow(dead_code)]
|
||||||
|
brace_token: token::Brace,
|
||||||
|
|
||||||
|
fields: Punctuated<Field, Token![,]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
mod kw {
|
||||||
|
syn::custom_keyword!(commands);
|
||||||
|
syn::custom_keyword!(namespace);
|
||||||
|
syn::custom_keyword!(declare);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum Field {
|
||||||
|
Function(FunctionDeclaration),
|
||||||
|
Namespace(Namespace),
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Namespace {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
namespace_token: kw::namespace,
|
||||||
|
|
||||||
|
path: NamespacePath,
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
brace_token: token::Brace,
|
||||||
|
|
||||||
|
fields: Punctuated<Field, Token![,]>,
|
||||||
|
}
|
||||||
|
type NamespacePath = Punctuated<Ident, Token![::]>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct FunctionDeclaration {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
function_token: kw::declare,
|
||||||
|
|
||||||
|
name: Ident,
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
colon_token: Token![:],
|
||||||
|
|
||||||
|
ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for DataCommandEnum {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let content;
|
||||||
|
Ok(DataCommandEnum {
|
||||||
|
commands_token: input.parse()?,
|
||||||
|
brace_token: braced!(content in input),
|
||||||
|
fields: content.parse_terminated(Field::parse, Token![,])?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Parse for Field {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
if input.peek(Token![#]) {
|
||||||
|
// FIXME(@soispha): We ignore doc comments, which should probably be replaced by adding
|
||||||
|
// them to the output <2023-09-19>
|
||||||
|
let _output = input.call(Attribute::parse_outer).unwrap_or(vec![]);
|
||||||
|
let lookahead = input.lookahead1();
|
||||||
|
|
||||||
|
if lookahead.peek(kw::namespace) {
|
||||||
|
input.parse().map(Field::Namespace)
|
||||||
|
} else if lookahead.peek(kw::declare) {
|
||||||
|
input.parse().map(Field::Function)
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if lookahead.peek(kw::declare) {
|
||||||
|
input.parse().map(Field::Function)
|
||||||
|
} else if lookahead.peek(kw::namespace) {
|
||||||
|
input.parse().map(Field::Namespace)
|
||||||
|
} else {
|
||||||
|
Err(lookahead.error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for FunctionDeclaration {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
Ok(FunctionDeclaration {
|
||||||
|
function_token: input.parse()?,
|
||||||
|
name: input.parse()?,
|
||||||
|
colon_token: input.parse()?,
|
||||||
|
ty: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Parse for Namespace {
|
||||||
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
|
let content;
|
||||||
|
Ok(Namespace {
|
||||||
|
namespace_token: input.parse()?,
|
||||||
|
path: NamespacePath::parse_separated_nonempty(input)?,
|
||||||
|
brace_token: braced!(content in input),
|
||||||
|
fields: content.parse_terminated(Field::parse, Token![,])?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn parse_command_enum(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DataCommandEnum);
|
||||||
|
|
||||||
// Build the language wrappers
|
// Build the language wrappers
|
||||||
let lua_wrapper: TokenStream2 = generate::lua_wrapper(&input);
|
let lua_wrapper: TokenStream2 = generate::lua_wrapper(&input);
|
||||||
|
@ -85,9 +192,9 @@ pub fn ci_command_enum(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
// Build the final enum
|
// Build the final enum
|
||||||
let command_enum = generate::command_enum(&input);
|
let command_enum = generate::command_enum(&input);
|
||||||
|
|
||||||
quote! {
|
let output = quote! {
|
||||||
#command_enum
|
#command_enum
|
||||||
#lua_wrapper
|
#lua_wrapper
|
||||||
}
|
};
|
||||||
.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Use `cargo expand app::command_interface::command_list` for an overview of the file contents
|
||||||
|
|
||||||
|
use language_macros::parse_command_enum;
|
||||||
|
|
||||||
|
// TODO(@soispha): Should these paths be moved to the proc macro?
|
||||||
|
// As they are not static, it could be easier for other people,
|
||||||
|
// if they stay here.
|
||||||
|
use crate::app::command_interface::command_transfer_value::CommandTransferValue;
|
||||||
|
use crate::app::Event;
|
||||||
|
use mlua::IntoLua;
|
||||||
|
|
||||||
|
parse_command_enum! {
|
||||||
|
commands {
|
||||||
|
/// 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>
|
||||||
|
declare print: fn(CommandTransferValue),
|
||||||
|
|
||||||
|
namespace trinitrix {
|
||||||
|
/// 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>),
|
||||||
|
|
||||||
|
/// 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(),
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Functions only used internally within Name
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,13 @@ use tokio::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
command_interface::{add_lua_functions_to_globals, Command},
|
command_interface::{
|
||||||
|
add_lua_functions_to_globals,
|
||||||
|
Api::Raw,
|
||||||
|
Command,
|
||||||
|
Raw::{DisplayOutput, RaiseError},
|
||||||
|
Trinitrix::Api,
|
||||||
|
},
|
||||||
events::event_types::Event,
|
events::event_types::Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -87,7 +93,10 @@ async fn exec_lua(lua_code: &str, event_call_tx: mpsc::Sender<Event>) -> Result<
|
||||||
|
|
||||||
if output != "nil" {
|
if output != "nil" {
|
||||||
event_call_tx
|
event_call_tx
|
||||||
.send(Event::CommandEvent(Command::DisplayOutput(output), None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Api(Raw(DisplayOutput(output)))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await
|
.await
|
||||||
.context("Failed to send lua output command")?
|
.context("Failed to send lua output command")?
|
||||||
}
|
}
|
||||||
|
@ -96,7 +105,7 @@ async fn exec_lua(lua_code: &str, event_call_tx: mpsc::Sender<Event>) -> Result<
|
||||||
error!("Lua code `{}` returned error: `{}`", lua_code, err);
|
error!("Lua code `{}` returned error: `{}`", lua_code, err);
|
||||||
event_call_tx
|
event_call_tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(
|
||||||
Command::RaiseError(err.to_string()),
|
Command::Trinitrix(Api(Raw(RaiseError(err.to_string())))),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1,64 +1,5 @@
|
||||||
// Use `cargo expand app::command_interface` for an overview of the file contents
|
|
||||||
|
|
||||||
pub mod command_transfer_value;
|
pub mod command_transfer_value;
|
||||||
pub mod lua_command_manager;
|
pub mod lua_command_manager;
|
||||||
|
pub mod command_list;
|
||||||
|
|
||||||
use language_macros::ci_command_enum;
|
pub use command_list::*;
|
||||||
|
|
||||||
// TODO(@Soispha): Should these paths be moved to the proc macro?
|
|
||||||
// As they are not static, it could be easier for other people,
|
|
||||||
// if they stay here.
|
|
||||||
use crate::app::command_interface::command_transfer_value::CommandTransferValue;
|
|
||||||
use crate::app::Event;
|
|
||||||
use mlua::IntoLua;
|
|
||||||
|
|
||||||
#[ci_command_enum]
|
|
||||||
struct Commands {
|
|
||||||
/// 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>
|
|
||||||
print: fn(CommandTransferValue),
|
|
||||||
|
|
||||||
// Begin debug functions
|
|
||||||
/// Greets the user
|
|
||||||
greet: fn(String) -> String,
|
|
||||||
|
|
||||||
/// Returns a table of greeted users
|
|
||||||
greet_multiple: fn() -> Table,
|
|
||||||
// End debug functions
|
|
||||||
/// Closes the application
|
|
||||||
exit: fn(),
|
|
||||||
|
|
||||||
/// Shows the command line
|
|
||||||
command_line_show: fn(),
|
|
||||||
|
|
||||||
/// Hides the command line
|
|
||||||
command_line_hide: fn(),
|
|
||||||
|
|
||||||
/// Go to the next plane
|
|
||||||
cycle_planes: fn(),
|
|
||||||
/// Go to the previous plane
|
|
||||||
cycle_planes_rev: fn(),
|
|
||||||
|
|
||||||
/// Sets the current app mode to Normal / navigation mode
|
|
||||||
set_mode_normal: fn(),
|
|
||||||
/// Sets the current app mode to Insert / editing mode
|
|
||||||
set_mode_insert: fn(),
|
|
||||||
|
|
||||||
/// Send a message to the current room
|
|
||||||
/// The send message is interpreted literally.
|
|
||||||
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
|
|
||||||
help: fn(Option<String>),
|
|
||||||
|
|
||||||
/// Send an error to the default error output
|
|
||||||
raise_error: fn(String),
|
|
||||||
/// Send output to the default output
|
|
||||||
/// This is mainly used to display the final
|
|
||||||
/// output of evaluated lua commands.
|
|
||||||
display_output: fn(String),
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
app::{
|
app::{
|
||||||
command_interface::{
|
command_interface::{
|
||||||
command_transfer_value::{CommandTransferValue, Table},
|
command_transfer_value::{CommandTransferValue, Table},
|
||||||
Command,
|
Api, Command, Debug, Raw, Trinitrix, Ui,
|
||||||
},
|
},
|
||||||
events::event_types::EventStatus,
|
events::event_types::EventStatus,
|
||||||
status::State,
|
status::State,
|
||||||
|
@ -66,75 +66,19 @@ pub async fn handle(
|
||||||
trace!("Handling command: {:#?}", command);
|
trace!("Handling command: {:#?}", command);
|
||||||
|
|
||||||
Ok(match command {
|
Ok(match command {
|
||||||
Command::Exit => {
|
|
||||||
send_status_output!("Terminating the application..");
|
|
||||||
EventStatus::Terminate
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::DisplayOutput(output) => {
|
|
||||||
// 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
|
|
||||||
// better, should it?
|
|
||||||
send_status_output!(output);
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
Command::Print(output) => {
|
Command::Print(output) => {
|
||||||
let output_str: String = output.to_string();
|
let output_str: String = output.to_string();
|
||||||
send_status_output!(output_str);
|
send_status_output!(output_str);
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
|
|
||||||
Command::CommandLineShow => {
|
Command::Trinitrix(trinitrix) => match trinitrix {
|
||||||
app.ui.cli_enable();
|
Trinitrix::Debug(debug) => match debug {
|
||||||
app.status.set_state(State::Command);
|
Debug::Greet(msg) => {
|
||||||
send_status_output!("CLI online");
|
send_main_output!("Greeting, {}!", msg);
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Command::CommandLineHide => {
|
Debug::GreetMultiple => {
|
||||||
app.ui.cli_disable();
|
|
||||||
send_status_output!("CLI offline");
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::CyclePlanes => {
|
|
||||||
app.ui.cycle_main_input_position();
|
|
||||||
send_status_output!("Switched main input position");
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
Command::CyclePlanesRev => {
|
|
||||||
app.ui.cycle_main_input_position_rev();
|
|
||||||
send_status_output!("Switched main input position; reversed");
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::SetModeNormal => {
|
|
||||||
app.status.set_state(State::Normal);
|
|
||||||
send_status_output!("Set input mode to Normal");
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
Command::SetModeInsert => {
|
|
||||||
app.status.set_state(State::Insert);
|
|
||||||
app.ui.set_input_position(InputPosition::MessageCompose);
|
|
||||||
send_status_output!("Set input mode to Insert");
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
|
|
||||||
Command::RoomMessageSend(msg) => {
|
|
||||||
if let Some(room) = app.status.room_mut() {
|
|
||||||
room.send(msg.clone()).await?;
|
|
||||||
send_status_output!("Sent message: `{}`", msg);
|
|
||||||
} else {
|
|
||||||
// TODO(@Soispha): Should this raise a Lua error? It could be very confusing,
|
|
||||||
// when a user doesn't read the log.
|
|
||||||
warn!("Can't send message: `{}`, as there is no open room!", &msg);
|
|
||||||
}
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
Command::Greet(name) => {
|
|
||||||
send_main_output!("Hi, {}!", name);
|
|
||||||
EventStatus::Ok
|
|
||||||
}
|
|
||||||
Command::GreetMultiple => {
|
|
||||||
let mut table: Table = HashMap::new();
|
let mut table: Table = HashMap::new();
|
||||||
table.insert("UserId".to_owned(), CommandTransferValue::Integer(2));
|
table.insert("UserId".to_owned(), CommandTransferValue::Integer(2));
|
||||||
table.insert(
|
table.insert(
|
||||||
|
@ -152,10 +96,72 @@ pub async fn handle(
|
||||||
send_main_output!(table);
|
send_main_output!(table);
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Command::Help(_) => todo!(),
|
},
|
||||||
Command::RaiseError(err) => {
|
Trinitrix::Api(api) => match api {
|
||||||
|
Api::Exit => {
|
||||||
|
send_status_output!("Terminating the application..");
|
||||||
|
EventStatus::Terminate
|
||||||
|
}
|
||||||
|
Api::RoomMessageSend(msg) => {
|
||||||
|
if let Some(room) = app.status.room_mut() {
|
||||||
|
room.send(msg.clone()).await?;
|
||||||
|
send_status_output!("Sent message: `{}`", msg);
|
||||||
|
} else {
|
||||||
|
// // FIXME(@soispha): This should raise an error within lua, as it would
|
||||||
|
// otherwise be very confusing <2023-09-20>
|
||||||
|
warn!("Can't send message: `{}`, as there is no open room!", &msg);
|
||||||
|
}
|
||||||
|
EventStatus::Ok
|
||||||
|
}
|
||||||
|
Api::Help(_) => todo!(),
|
||||||
|
Api::Ui(ui) => match ui {
|
||||||
|
Ui::CommandLineShow => {
|
||||||
|
app.ui.cli_enable();
|
||||||
|
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);
|
||||||
|
send_status_output!("Set input mode to Normal");
|
||||||
|
EventStatus::Ok
|
||||||
|
}
|
||||||
|
Ui::SetModeInsert => {
|
||||||
|
app.status.set_state(State::Insert);
|
||||||
|
app.ui.set_input_position(InputPosition::MessageCompose);
|
||||||
|
send_status_output!("Set input mode to Insert");
|
||||||
|
EventStatus::Ok
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Api::Raw(raw) => match raw {
|
||||||
|
Raw::RaiseError(err) => {
|
||||||
send_error_output!(err);
|
send_error_output!(err);
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
|
Raw::DisplayOutput(output) => {
|
||||||
|
// 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
|
||||||
|
// better, should it?
|
||||||
|
send_status_output!(output);
|
||||||
|
EventStatus::Ok
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers}
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
command_interface::Command,
|
command_interface::{Api, Command, Trinitrix, Ui},
|
||||||
events::event_types::{Event, EventStatus},
|
events::event_types::{Event, EventStatus},
|
||||||
App,
|
App,
|
||||||
},
|
},
|
||||||
|
@ -20,7 +20,10 @@ pub async fn handle_command(
|
||||||
code: KeyCode::Esc, ..
|
code: KeyCode::Esc, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::SetModeNormal, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Ui(Ui::SetModeNormal))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -62,14 +65,20 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::Exit, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Exit)),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
code: KeyCode::Tab, ..
|
code: KeyCode::Tab, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::CyclePlanes, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Ui(Ui::CyclePlanes))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -77,7 +86,10 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::CyclePlanesRev, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Ui(Ui::CyclePlanesRev))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -85,7 +97,10 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::CommandLineShow, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Ui(Ui::CommandLineShow))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -93,7 +108,10 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::SetModeInsert, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Ui(Ui::SetModeInsert))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
input => match app.ui.input_position() {
|
input => match app.ui.input_position() {
|
||||||
|
@ -193,7 +211,10 @@ pub async fn handle_insert(app: &mut App<'_>, event: &CrosstermEvent) -> Result<
|
||||||
code: KeyCode::Esc, ..
|
code: KeyCode::Esc, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::SetModeNormal, None))
|
.send(Event::CommandEvent(
|
||||||
|
Command::Trinitrix(Trinitrix::Api(Api::Ui(Ui::SetModeNormal))),
|
||||||
|
None,
|
||||||
|
))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -203,7 +224,9 @@ pub async fn handle_insert(app: &mut App<'_>, event: &CrosstermEvent) -> Result<
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(
|
||||||
Command::RoomMessageSend(app.ui.message_compose.lines().join("\n")),
|
Command::Trinitrix(Trinitrix::Api(Api::RoomMessageSend(
|
||||||
|
app.ui.message_compose.lines().join("\n"),
|
||||||
|
))),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
Loading…
Reference in New Issue