Compare commits
No commits in common. "6745da4c715f56ac314429c3f567f64f36e3492a" and "29aa6c1d3365d649e149928038c94b3c46c5a740" have entirely different histories.
6745da4c71
...
29aa6c1d33
|
@ -1,96 +1,50 @@
|
||||||
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, ToTokens};
|
use quote::{format_ident, quote};
|
||||||
use syn::{punctuated::Punctuated, Token, Type, Ident};
|
use syn::{DeriveInput, Field, Type};
|
||||||
|
|
||||||
use crate::{DataCommandEnum, Field};
|
use super::{get_input_type_of_bare_fn_field, parse_derive_input_as_named_fields};
|
||||||
|
|
||||||
use super::get_input_type_of_bare_fn_field;
|
pub fn command_enum(input: &DeriveInput) -> TokenStream2 {
|
||||||
|
let named_fields = parse_derive_input_as_named_fields(input);
|
||||||
pub fn command_enum(input: &DataCommandEnum) -> TokenStream2 {
|
let fields: TokenStream2 = named_fields
|
||||||
let (fields, namespace_enums): (TokenStream2, TokenStream2) =
|
.named
|
||||||
turn_fields_to_enum(&input.fields);
|
.iter()
|
||||||
|
.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_fields_to_enum(fields: &Punctuated<Field, Token![,]>) -> (TokenStream2, TokenStream2) {
|
fn turn_struct_fieled_to_enum(field: &Field) -> TokenStream2 {
|
||||||
let output: Vec<_> = fields
|
let field_name = format_ident!(
|
||||||
.iter()
|
"{}",
|
||||||
.map(|field| turn_struct_field_to_enum(field))
|
field
|
||||||
.collect();
|
.ident
|
||||||
|
.as_ref()
|
||||||
|
.expect("These are named fields, it should be Some(<name>)")
|
||||||
|
.to_string()
|
||||||
|
.from_case(Case::Snake)
|
||||||
|
.to_case(Case::Pascal)
|
||||||
|
);
|
||||||
|
|
||||||
let mut fields_output: TokenStream2 = Default::default();
|
let input_type: Option<Type> = get_input_type_of_bare_fn_field(field);
|
||||||
let mut namespace_enums_output: TokenStream2 = Default::default();
|
|
||||||
|
|
||||||
for (fields, namespace_enum) in output {
|
match input_type {
|
||||||
fields_output.extend(fields.to_token_stream());
|
Some(input_type) => {
|
||||||
namespace_enums_output.extend(namespace_enum.to_token_stream());
|
quote! {
|
||||||
}
|
#field_name(#input_type),
|
||||||
|
|
||||||
(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!(
|
|
||||||
"{}",
|
|
||||||
fun_field
|
|
||||||
.name
|
|
||||||
.to_string()
|
|
||||||
.from_case(Case::Snake)
|
|
||||||
.to_case(Case::Pascal)
|
|
||||||
);
|
|
||||||
|
|
||||||
let input_type: Option<Type> = get_input_type_of_bare_fn_field(fun_field);
|
|
||||||
|
|
||||||
match input_type {
|
|
||||||
Some(input_type) => (
|
|
||||||
quote! {
|
|
||||||
#field_name(#input_type),
|
|
||||||
},
|
|
||||||
quote! {},
|
|
||||||
),
|
|
||||||
None => (
|
|
||||||
quote! {
|
|
||||||
#field_name,
|
|
||||||
},
|
|
||||||
quote! {},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Field::Namespace(namespace) => {
|
None => {
|
||||||
let (namespace_output_fields, namespace_output_namespace_enums) =
|
quote! {
|
||||||
turn_fields_to_enum(&namespace.fields);
|
#field_name,
|
||||||
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,35 +1,16 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use syn::{punctuated::Punctuated, Token};
|
use syn::{DeriveInput, Field};
|
||||||
|
|
||||||
use crate::{DataCommandEnum, Field, FunctionDeclaration, NamespacePath};
|
use crate::generate::parse_derive_input_as_named_fields;
|
||||||
|
|
||||||
pub fn generate_add_lua_functions_to_globals(input: &DataCommandEnum) -> TokenStream2 {
|
pub fn generate_add_lua_functions_to_globals(input: &DeriveInput) -> TokenStream2 {
|
||||||
fn turn_field_to_functions(
|
let named_fields = parse_derive_input_as_named_fields(input);
|
||||||
input: &Punctuated<Field, Token![,]>,
|
let function_adders: TokenStream2 = named_fields
|
||||||
namespace_path: Option<&NamespacePath>,
|
.named
|
||||||
) -> TokenStream2 {
|
.iter()
|
||||||
input
|
.map(|field| generate_function_adder(field))
|
||||||
.iter()
|
.collect();
|
||||||
.map(|field| match field {
|
|
||||||
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(
|
||||||
|
@ -47,67 +28,15 @@ pub fn generate_add_lua_functions_to_globals(input: &DataCommandEnum) -> TokenSt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_function_adder(
|
fn generate_function_adder(field: &Field) -> TokenStream2 {
|
||||||
field: &FunctionDeclaration,
|
let field_ident = field
|
||||||
namespace_path: Option<&NamespacePath>,
|
.ident
|
||||||
) -> TokenStream2 {
|
.as_ref()
|
||||||
let field_ident = &field.name;
|
.expect("This is should be a named field");
|
||||||
|
|
||||||
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(
|
||||||
|
@ -116,7 +45,10 @@ fn generate_function_adder(
|
||||||
#function_name
|
#function_name
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
#setter
|
|
||||||
|
globals.set(#function_name, #function_ident).expect(
|
||||||
|
"Setting a static global value should work"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
|
||||||
use crate::{
|
use crate::generate::lua_wrapper::{
|
||||||
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: &DataCommandEnum) -> TokenStream2 {
|
pub fn lua_wrapper(input: &DeriveInput) -> 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(None, input);
|
let rust_wrapper_functions = generate_rust_wrapper_functions(input);
|
||||||
quote! {
|
quote! {
|
||||||
#add_lua_functions_to_globals
|
#add_lua_functions_to_globals
|
||||||
#rust_wrapper_functions
|
#rust_wrapper_functions
|
||||||
|
|
|
@ -1,39 +1,21 @@
|
||||||
use convert_case::{Case, Casing};
|
use convert_case::{Case, Casing};
|
||||||
use proc_macro2::TokenStream as TokenStream2;
|
use proc_macro2::TokenStream as TokenStream2;
|
||||||
use quote::quote;
|
use quote::{format_ident, quote};
|
||||||
use syn::{punctuated::Punctuated, token::Comma, GenericArgument, Lifetime, Token, Type};
|
use syn::{
|
||||||
|
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn generate_rust_wrapper_functions(
|
use crate::generate::{
|
||||||
namespace: Option<&NamespacePath>,
|
get_input_type_of_bare_fn_field, get_return_type_of_bare_fn_field,
|
||||||
input: &DataCommandEnum,
|
parse_derive_input_as_named_fields,
|
||||||
) -> TokenStream2 {
|
};
|
||||||
generate_rust_wrapper_functions_rec(namespace, &input.fields)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn generate_rust_wrapper_functions_rec(
|
pub fn generate_rust_wrapper_functions(input: &DeriveInput) -> TokenStream2 {
|
||||||
namespace: Option<&NamespacePath>,
|
let named_fields = parse_derive_input_as_named_fields(input);
|
||||||
input: &Punctuated<Field, Token![,]>,
|
let wrapped_functions: TokenStream2 = named_fields
|
||||||
) -> TokenStream2 {
|
.named
|
||||||
let wrapped_functions: TokenStream2 = input
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|field| match field {
|
.map(|field| wrap_lua_function(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! {
|
||||||
|
@ -41,12 +23,12 @@ pub fn generate_rust_wrapper_functions_rec(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wrap_lua_function(namespace: &NamespacePath, field: &FunctionDeclaration) -> TokenStream2 {
|
fn wrap_lua_function(field: &Field) -> 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.name;
|
let function_name = field.ident.as_ref().expect("This should be a named field");
|
||||||
let function_body = get_function_body(&namespace, field, input_type.is_some(), &return_type);
|
let function_body = get_function_body(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);
|
||||||
|
@ -116,60 +98,30 @@ fn get_and_add_lifetimes_form_inputs_and_outputs<'a>(
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_function_body(
|
fn get_function_body(field: &Field, has_input: bool, output_type: &Option<Type>) -> TokenStream2 {
|
||||||
namespace: &NamespacePath,
|
|
||||||
field: &FunctionDeclaration,
|
|
||||||
has_input: bool,
|
|
||||||
output_type: &Option<Type>,
|
|
||||||
) -> TokenStream2 {
|
|
||||||
let command_name = field
|
let command_name = field
|
||||||
.name
|
.ident
|
||||||
|
.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 command_ident = {
|
let send_output = if has_input {
|
||||||
if has_input {
|
quote! {
|
||||||
format!("{}(", command_name)
|
Event::CommandEvent(
|
||||||
} else {
|
Command::#command_ident(input.clone()),
|
||||||
command_name.clone()
|
Some(callback_tx),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote! {
|
||||||
|
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 {
|
||||||
|
|
|
@ -3,9 +3,18 @@ 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::{ReturnType, Type, TypeBareFn};
|
use syn::{DeriveInput, Field, FieldsNamed, ReturnType, Type, TypeBareFn};
|
||||||
|
|
||||||
use crate::FunctionDeclaration;
|
pub fn parse_derive_input_as_named_fields(input: &DeriveInput) -> FieldsNamed {
|
||||||
|
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 {
|
||||||
|
@ -28,7 +37,7 @@ pub fn get_bare_fn_input_type(function: &TypeBareFn) -> Option<Type> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_input_type_of_bare_fn_field(field: &FunctionDeclaration) -> Option<Type> {
|
pub fn get_input_type_of_bare_fn_field(field: &Field) -> 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!(
|
||||||
|
@ -37,7 +46,7 @@ pub fn get_input_type_of_bare_fn_field(field: &FunctionDeclaration) -> Option<Ty
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_return_type_of_bare_fn_field(field: &FunctionDeclaration) -> Option<Type> {
|
pub fn get_return_type_of_bare_fn_field(field: &Field) -> 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,10 +1,7 @@
|
||||||
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::{
|
use syn::DeriveInput;
|
||||||
braced, parse::Parse, parse_macro_input, punctuated::Punctuated, token, Attribute, Ident,
|
|
||||||
Token, Type,
|
|
||||||
};
|
|
||||||
|
|
||||||
mod generate;
|
mod generate;
|
||||||
|
|
||||||
|
@ -17,17 +14,19 @@ 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 input to the `parse_command_enum` proc macro.
|
/// the `Commands` struct.
|
||||||
|
/// 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:
|
||||||
/// ```no_run
|
/// ```rust
|
||||||
/// parse_command_enum! {
|
/// #[ci_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:
|
||||||
/// ```no_run
|
/// ```rust
|
||||||
/// #[derive(Debug)]
|
/// #[derive(Debug)]
|
||||||
/// pub enum Command {
|
/// pub enum Command {
|
||||||
/// Greet(String),
|
/// Greet(String),
|
||||||
|
@ -73,118 +72,12 @@ mod generate;
|
||||||
/// };
|
/// };
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Debug)]
|
#[proc_macro_attribute]
|
||||||
struct DataCommandEnum {
|
pub fn ci_command_enum(_attrs: TokenStream, input: TokenStream) -> TokenStream {
|
||||||
#[allow(dead_code)]
|
// Construct a representation of Rust code as a syntax tree
|
||||||
commands_token: kw::commands,
|
// that we can manipulate
|
||||||
|
let input: DeriveInput = syn::parse(input)
|
||||||
#[allow(dead_code)]
|
.expect("This should always be valid rust code, as it's extracted from direct 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);
|
||||||
|
@ -192,9 +85,9 @@ pub fn parse_command_enum(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);
|
||||||
|
|
||||||
let output = quote! {
|
quote! {
|
||||||
#command_enum
|
#command_enum
|
||||||
#lua_wrapper
|
#lua_wrapper
|
||||||
};
|
}
|
||||||
output.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,8 @@ impl AccountsManager {
|
||||||
return match config {
|
return match config {
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
info!("Loading serialized AccountsManager");
|
info!("Loading serialized AccountsManager");
|
||||||
let accounts_data: AccountsData = serde_json::from_str(&s)?;
|
let accounts_data: AccountsData =
|
||||||
|
serde_json::from_str(&s)?;
|
||||||
let mut clients = Vec::new();
|
let mut clients = Vec::new();
|
||||||
clients.resize(accounts_data.accounts.len(), None);
|
clients.resize(accounts_data.accounts.len(), None);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -93,12 +94,12 @@ impl AccountsManager {
|
||||||
|
|
||||||
let session = match client.session() {
|
let session = match client.session() {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
None => return Err(Error::msg("Failed to get session")),
|
None => return Err(Error::msg("Failed to get session"))
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = match client.account().get_display_name().await? {
|
let name = match client.account().get_display_name().await? {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
None => return Err(Error::msg("Failed to get display name")),
|
None => return Err(Error::msg("Failed to get display name"))
|
||||||
};
|
};
|
||||||
|
|
||||||
let account = Account {
|
let account = Account {
|
||||||
|
|
|
@ -1,73 +0,0 @@
|
||||||
// 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 mlua::IntoLua;
|
|
||||||
use crate::app::command_interface::command_transfer_value::CommandTransferValue;
|
|
||||||
use crate::app::Event;
|
|
||||||
|
|
||||||
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),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
use std::{collections::HashMap, fmt::Display};
|
use std::{collections::HashMap, fmt::Display};
|
||||||
|
|
||||||
|
use mlua::FromLua;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod type_conversions;
|
pub mod type_conversions;
|
||||||
|
|
|
@ -11,13 +11,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
command_interface::{
|
command_interface::{add_lua_functions_to_globals, Command},
|
||||||
add_lua_functions_to_globals,
|
|
||||||
Api::Raw,
|
|
||||||
Command,
|
|
||||||
Raw::{DisplayOutput, RaiseError},
|
|
||||||
Trinitrix::Api,
|
|
||||||
},
|
|
||||||
events::event_types::Event,
|
events::event_types::Event,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -93,10 +87,7 @@ 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(
|
.send(Event::CommandEvent(Command::DisplayOutput(output), None))
|
||||||
Command::Trinitrix(Api(Raw(DisplayOutput(output)))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await
|
.await
|
||||||
.context("Failed to send lua output command")?
|
.context("Failed to send lua output command")?
|
||||||
}
|
}
|
||||||
|
@ -105,7 +96,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::Trinitrix(Api(Raw(RaiseError(err.to_string())))),
|
Command::RaiseError(err.to_string()),
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -1,5 +1,64 @@
|
||||||
pub mod command_list;
|
// 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 use command_list::*;
|
use language_macros::ci_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;
|
||||||
|
|
||||||
|
#[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},
|
||||||
Api, Command, Debug, Raw, Trinitrix, Ui,
|
Command,
|
||||||
},
|
},
|
||||||
events::event_types::EventStatus,
|
events::event_types::EventStatus,
|
||||||
status::State,
|
status::State,
|
||||||
|
@ -66,102 +66,96 @@ 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::Trinitrix(trinitrix) => match trinitrix {
|
Command::CommandLineShow => {
|
||||||
Trinitrix::Debug(debug) => match debug {
|
app.ui.cli_enable();
|
||||||
Debug::Greet(msg) => {
|
app.status.set_state(State::Command);
|
||||||
send_main_output!("Greeting, {}!", msg);
|
send_status_output!("CLI online");
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Debug::GreetMultiple => {
|
Command::CommandLineHide => {
|
||||||
let mut table: Table = HashMap::new();
|
app.ui.cli_disable();
|
||||||
table.insert("UserId".to_owned(), CommandTransferValue::Integer(2));
|
send_status_output!("CLI offline");
|
||||||
table.insert(
|
EventStatus::Ok
|
||||||
"UserName".to_owned(),
|
}
|
||||||
CommandTransferValue::String("James".to_owned()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut second_table: Table = HashMap::new();
|
Command::CyclePlanes => {
|
||||||
second_table.insert("interface".to_owned(), CommandTransferValue::Integer(3));
|
app.ui.cycle_main_input_position();
|
||||||
second_table.insert("api".to_owned(), CommandTransferValue::Boolean(true));
|
send_status_output!("Switched main input position");
|
||||||
table.insert(
|
EventStatus::Ok
|
||||||
"Versions".to_owned(),
|
}
|
||||||
CommandTransferValue::Table(second_table),
|
Command::CyclePlanesRev => {
|
||||||
);
|
app.ui.cycle_main_input_position_rev();
|
||||||
send_main_output!(table);
|
send_status_output!("Switched main input position; reversed");
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Trinitrix::Api(api) => match api {
|
Command::SetModeNormal => {
|
||||||
Api::Exit => {
|
app.status.set_state(State::Normal);
|
||||||
send_status_output!("Terminating the application..");
|
send_status_output!("Set input mode to Normal");
|
||||||
EventStatus::Terminate
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
Api::RoomMessageSend(msg) => {
|
Command::SetModeInsert => {
|
||||||
if let Some(room) = app.status.room_mut() {
|
app.status.set_state(State::Insert);
|
||||||
room.send(msg.clone()).await?;
|
app.ui.set_input_position(InputPosition::MessageCompose);
|
||||||
send_status_output!("Sent message: `{}`", msg);
|
send_status_output!("Set input mode to Insert");
|
||||||
} else {
|
EventStatus::Ok
|
||||||
// // 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);
|
Command::RoomMessageSend(msg) => {
|
||||||
}
|
if let Some(room) = app.status.room_mut() {
|
||||||
EventStatus::Ok
|
room.send(msg.clone()).await?;
|
||||||
}
|
send_status_output!("Sent message: `{}`", msg);
|
||||||
Api::Help(_) => todo!(),
|
} else {
|
||||||
Api::Ui(ui) => match ui {
|
// TODO(@Soispha): Should this raise a Lua error? It could be very confusing,
|
||||||
Ui::CommandLineShow => {
|
// when a user doesn't read the log.
|
||||||
app.ui.cli_enable();
|
warn!("Can't send message: `{}`, as there is no open room!", &msg);
|
||||||
app.status.set_state(State::Command);
|
}
|
||||||
send_status_output!("CLI online");
|
EventStatus::Ok
|
||||||
EventStatus::Ok
|
}
|
||||||
}
|
Command::Greet(name) => {
|
||||||
Ui::CommandLineHide => {
|
send_main_output!("Hi, {}!", name);
|
||||||
app.ui.cli_disable();
|
EventStatus::Ok
|
||||||
send_status_output!("CLI offline");
|
}
|
||||||
EventStatus::Ok
|
Command::GreetMultiple => {
|
||||||
}
|
let mut table: Table = HashMap::new();
|
||||||
Ui::CyclePlanes => {
|
table.insert("UserId".to_owned(), CommandTransferValue::Integer(2));
|
||||||
app.ui.cycle_main_input_position();
|
table.insert(
|
||||||
send_status_output!("Switched main input position");
|
"UserName".to_owned(),
|
||||||
EventStatus::Ok
|
CommandTransferValue::String("James".to_owned()),
|
||||||
}
|
);
|
||||||
Ui::CyclePlanesRev => {
|
|
||||||
app.ui.cycle_main_input_position_rev();
|
let mut second_table: Table = HashMap::new();
|
||||||
send_status_output!("Switched main input position; reversed");
|
second_table.insert("interface".to_owned(), CommandTransferValue::Integer(3));
|
||||||
EventStatus::Ok
|
second_table.insert("api".to_owned(), CommandTransferValue::Boolean(true));
|
||||||
}
|
table.insert(
|
||||||
Ui::SetModeNormal => {
|
"Versions".to_owned(),
|
||||||
app.status.set_state(State::Normal);
|
CommandTransferValue::Table(second_table),
|
||||||
send_status_output!("Set input mode to Normal");
|
);
|
||||||
EventStatus::Ok
|
send_main_output!(table);
|
||||||
}
|
EventStatus::Ok
|
||||||
Ui::SetModeInsert => {
|
}
|
||||||
app.status.set_state(State::Insert);
|
Command::Help(_) => todo!(),
|
||||||
app.ui.set_input_position(InputPosition::MessageCompose);
|
Command::RaiseError(err) => {
|
||||||
send_status_output!("Set input mode to Insert");
|
send_error_output!(err);
|
||||||
EventStatus::Ok
|
EventStatus::Ok
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Api::Raw(raw) => match raw {
|
|
||||||
Raw::RaiseError(err) => {
|
|
||||||
send_error_output!(err);
|
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,10 @@ use crate::app::{events::event_types::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;
|
||||||
|
|
|
@ -3,12 +3,7 @@ use crossterm::event::{Event as CrosstermEvent, KeyCode, KeyEvent, KeyModifiers}
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app::{
|
app::{
|
||||||
command_interface::{
|
command_interface::Command,
|
||||||
Api::{Exit, Ui, RoomMessageSend},
|
|
||||||
Command,
|
|
||||||
Trinitrix::Api,
|
|
||||||
Ui::{CommandLineShow, CyclePlanes, CyclePlanesRev, SetModeInsert, SetModeNormal},
|
|
||||||
},
|
|
||||||
events::event_types::{Event, EventStatus},
|
events::event_types::{Event, EventStatus},
|
||||||
App,
|
App,
|
||||||
},
|
},
|
||||||
|
@ -25,10 +20,7 @@ pub async fn handle_command(
|
||||||
code: KeyCode::Esc, ..
|
code: KeyCode::Esc, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(Command::SetModeNormal, None))
|
||||||
Command::Trinitrix(Api(Ui(SetModeNormal))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -70,17 +62,14 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(Command::Trinitrix(Api(Exit)), None))
|
.send(Event::CommandEvent(Command::Exit, None))
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
code: KeyCode::Tab, ..
|
code: KeyCode::Tab, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(Command::CyclePlanes, None))
|
||||||
Command::Trinitrix(Api(Ui(CyclePlanes))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -88,10 +77,7 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(Command::CyclePlanesRev, None))
|
||||||
Command::Trinitrix(Api(Ui(CyclePlanesRev))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -99,10 +85,7 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(Command::CommandLineShow, None))
|
||||||
Command::Trinitrix(Api(Ui(CommandLineShow))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -110,10 +93,7 @@ pub async fn handle_normal(app: &mut App<'_>, input_event: &CrosstermEvent) -> R
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(Command::SetModeInsert, None))
|
||||||
Command::Trinitrix(Api(Ui(SetModeInsert))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
input => match app.ui.input_position() {
|
input => match app.ui.input_position() {
|
||||||
|
@ -213,10 +193,7 @@ pub async fn handle_insert(app: &mut App<'_>, event: &CrosstermEvent) -> Result<
|
||||||
code: KeyCode::Esc, ..
|
code: KeyCode::Esc, ..
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(Command::SetModeNormal, None))
|
||||||
Command::Trinitrix(Api(Ui(SetModeNormal))),
|
|
||||||
None,
|
|
||||||
))
|
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
CrosstermEvent::Key(KeyEvent {
|
CrosstermEvent::Key(KeyEvent {
|
||||||
|
@ -226,9 +203,7 @@ pub async fn handle_insert(app: &mut App<'_>, event: &CrosstermEvent) -> Result<
|
||||||
}) => {
|
}) => {
|
||||||
app.tx
|
app.tx
|
||||||
.send(Event::CommandEvent(
|
.send(Event::CommandEvent(
|
||||||
Command::Trinitrix(Api(RoomMessageSend(
|
Command::RoomMessageSend(app.ui.message_compose.lines().join("\n")),
|
||||||
app.ui.message_compose.lines().join("\n"),
|
|
||||||
))),
|
|
||||||
None,
|
None,
|
||||||
))
|
))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -3,7 +3,10 @@ use matrix_sdk::deserialized_responses::SyncResponse;
|
||||||
|
|
||||||
use crate::app::{events::event_types::EventStatus, App};
|
use crate::app::{events::event_types::EventStatus, App};
|
||||||
|
|
||||||
pub async fn handle(app: &mut App<'_>, sync: &SyncResponse) -> Result<EventStatus> {
|
pub async fn handle(
|
||||||
|
app: &mut App<'_>,
|
||||||
|
sync: &SyncResponse,
|
||||||
|
) -> Result<EventStatus> {
|
||||||
for (m_room_id, m_room) in sync.rooms.join.iter() {
|
for (m_room_id, m_room) in sync.rooms.join.iter() {
|
||||||
let room = match app.status.get_room_mut(m_room_id) {
|
let room = match app.status.get_room_mut(m_room_id) {
|
||||||
Some(r) => r,
|
Some(r) => r,
|
||||||
|
|
|
@ -6,7 +6,10 @@ use crate::{
|
||||||
ui::setup,
|
ui::setup,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn handle(app: &mut App<'_>, input_event: &CrosstermEvent) -> Result<EventStatus> {
|
pub async fn handle(
|
||||||
|
app: &mut App<'_>,
|
||||||
|
input_event: &CrosstermEvent,
|
||||||
|
) -> Result<EventStatus> {
|
||||||
let ui = match &mut app.ui.setup_ui {
|
let ui = match &mut app.ui.setup_ui {
|
||||||
Some(ui) => ui,
|
Some(ui) => ui,
|
||||||
None => bail!("SetupUI instance not found"),
|
None => bail!("SetupUI instance not found"),
|
||||||
|
|
|
@ -5,14 +5,16 @@ use cli_log::trace;
|
||||||
use crossterm::event::Event as CrosstermEvent;
|
use crossterm::event::Event as CrosstermEvent;
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use self::handlers::{command, lua_command, main, matrix, setup};
|
|
||||||
use super::EventStatus;
|
|
||||||
use crate::app::{
|
use crate::app::{
|
||||||
command_interface::{command_transfer_value::CommandTransferValue, Command},
|
command_interface::{command_transfer_value::CommandTransferValue, Command},
|
||||||
status::State,
|
status::State,
|
||||||
App,
|
App,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::handlers::{command, lua_command, main, matrix, setup};
|
||||||
|
|
||||||
|
use super::EventStatus;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Event {
|
pub enum Event {
|
||||||
InputEvent(CrosstermEvent),
|
InputEvent(CrosstermEvent),
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod event_status;
|
pub mod event_status;
|
||||||
|
|
||||||
pub use self::{event::*, event_status::*};
|
pub use self::event::*;
|
||||||
|
pub use self::event_status::*;
|
||||||
|
|
|
@ -12,7 +12,6 @@ use matrix_sdk::Client;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
|
|
||||||
use self::{command_interface::lua_command_manager::LuaCommandManager, events::event_types};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
accounts::{Account, AccountsManager},
|
accounts::{Account, AccountsManager},
|
||||||
app::{
|
app::{
|
||||||
|
@ -22,6 +21,8 @@ use crate::{
|
||||||
ui::{central, setup},
|
ui::{central, setup},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::{command_interface::lua_command_manager::LuaCommandManager, events::event_types};
|
||||||
|
|
||||||
pub struct App<'ui> {
|
pub struct App<'ui> {
|
||||||
ui: central::UI<'ui>,
|
ui: central::UI<'ui>,
|
||||||
accounts_manager: AccountsManager,
|
accounts_manager: AccountsManager,
|
||||||
|
@ -83,7 +84,10 @@ impl App<'_> {
|
||||||
.context("Failed to search for a config file")?
|
.context("Failed to search for a config file")?
|
||||||
{
|
{
|
||||||
lua_config_file = Some(config_dir.join("init.lua"));
|
lua_config_file = Some(config_dir.join("init.lua"));
|
||||||
info!("Found config dir: `{}`", config_dir.display());
|
info!(
|
||||||
|
"Found config dir: `{}`",
|
||||||
|
config_dir.display()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!("No config dir found!");
|
warn!("No config dir found!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ impl Status {
|
||||||
state: State::Normal,
|
state: State::Normal,
|
||||||
account_name: "".to_owned(),
|
account_name: "".to_owned(),
|
||||||
account_user_id: "".to_owned(),
|
account_user_id: "".to_owned(),
|
||||||
client,
|
client,
|
||||||
rooms,
|
rooms,
|
||||||
current_room_id: "".to_owned(),
|
current_room_id: "".to_owned(),
|
||||||
status_messages: vec![StatusMessage {
|
status_messages: vec![StatusMessage {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
mod accounts;
|
|
||||||
mod app;
|
mod app;
|
||||||
mod cli;
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
mod accounts;
|
||||||
|
mod cli;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,12 @@ use tui::{
|
||||||
Terminal,
|
Terminal,
|
||||||
};
|
};
|
||||||
use tui_textarea::TextArea;
|
use tui_textarea::TextArea;
|
||||||
pub use update::*;
|
|
||||||
|
use crate::ui::{terminal_prepare, textarea_inactivate, textarea_activate};
|
||||||
|
|
||||||
use super::setup;
|
use super::setup;
|
||||||
use crate::ui::{terminal_prepare, textarea_activate, textarea_inactivate};
|
|
||||||
|
pub use update::*;
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum InputPosition {
|
pub enum InputPosition {
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use tui::{
|
use tui::{layout::{Constraint, Direction, Layout}, widgets::{Paragraph, Block, Borders}, style::{Style, Color}};
|
||||||
layout::{Constraint, Direction, Layout},
|
|
||||||
style::{Color, Style},
|
use crate::app::status::Status;
|
||||||
widgets::{Block, Borders, Paragraph},
|
|
||||||
};
|
|
||||||
|
|
||||||
use self::widgets::{command_monitor, messages, room_info, rooms, status};
|
use self::widgets::{command_monitor, messages, room_info, rooms, status};
|
||||||
|
|
||||||
use super::UI;
|
use super::UI;
|
||||||
use crate::app::status::Status;
|
|
||||||
|
|
||||||
pub mod widgets;
|
pub mod widgets;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod command_monitor;
|
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
pub mod room_info;
|
pub mod room_info;
|
||||||
pub mod rooms;
|
pub mod rooms;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
pub mod command_monitor;
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
use tui::{
|
use tui::{
|
||||||
layout::Alignment,
|
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
text::Text,
|
text::Text,
|
||||||
widgets::{Block, Borders, Paragraph},
|
widgets::{Block, Borders, Paragraph}, layout::Alignment,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{app::status::Room, ui::central::InputPosition};
|
use crate::{app::status::Room, ui::central::InputPosition};
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use tui::{
|
use tui::{
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::Span,
|
text::Span,
|
||||||
widgets::{Block, Borders, List, ListItem},
|
widgets::{Borders, List, ListItem, Block},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{app::status::Status, ui::central::InputPosition};
|
use crate::{app::status::Status, ui::central::InputPosition};
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::io::Stdout;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use tui::{
|
use tui::{
|
||||||
backend::CrosstermBackend,
|
backend::CrosstermBackend,
|
||||||
layout::{Alignment, Constraint, Direction, Layout},
|
layout::{Constraint, Direction, Layout, Alignment},
|
||||||
style::{Color, Modifier, Style},
|
style::{Color, Modifier, Style},
|
||||||
text::Span,
|
text::Span,
|
||||||
widgets::{Block, Borders, Paragraph},
|
widgets::{Block, Borders, Paragraph},
|
||||||
|
|
Reference in New Issue