use proc_macro2::TokenStream as TokenStream2;
use quote::{format_ident, quote};
use crate::{
macros::config::trixy::TrixyConfig,
parser::command_spec::{Function, Identifier, NamedType, Namespace},
};
impl Function {
pub fn to_c(&self, config: &TrixyConfig, namespaces: &Vec<&Identifier>) -> TokenStream2 {
let ident = self.identifier.to_c_with_path(namespaces);
let inputs: Vec<TokenStream2> = self
.inputs
.iter()
.filter_map(|a| NamedType::to_c(a))
.collect();
let callback_function = format_ident!("{}", &config.callback_function);
let command_value: TokenStream2 = self.to_rust_path(&namespaces);
let no_output = || -> TokenStream2 {
quote! {
#[no_mangle]
pub extern "C" fn #ident(#(#inputs),*) -> core::ffi::c_int {
let error = std::panic::catch_unwind(|| {
crate :: #callback_function (crate :: #command_value);
0
});
if let Err(_) = error {
eprintln!("Catched a panic just before the c ffi.");
std::process::exit(1);
}
return 1;
}
}
};
if let Some(r#type) = &self.output {
if let Some(output_ident) = r#type.to_c() {
quote! {
#[no_mangle]
pub extern "C" fn #ident(output: *mut #output_ident, #(#inputs),*) -> core::ffi::c_int {
let output_val: #output_ident = {
let (tx, rx) = trixy::oneshot::channel();
let error = std::panic::catch_unwind(|| {
crate :: #callback_function (crate :: #command_value);
0
});
if let Err(_) = error {
eprintln!("Catched a panic just before the c ffi.");
std::process::exit(1);
}
let recv = rx.recv().expect("The channel should not be closed until this value is received");
recv.into()
};
unsafe {
std::ptr::write(output, output_val);
}
return 1;
}
}
} else {
no_output()
}
} else {
no_output()
}
}
pub fn to_rust_path(&self, namespaces: &Vec<&Identifier>) -> TokenStream2 {
let function_ident = self.to_rust_identifier(
|a| NamedType::to_rust_assignment(a).unwrap_or(TokenStream2::default()),
|_| {
quote! {
tx
}
},
);
if namespaces.is_empty() {
quote! {
Commands :: #function_ident
}
} else {
let nasp_pascal_ident = namespaces.last().expect("We checked").to_rust_pascalized();
let namespace_path: TokenStream2 = Namespace::to_rust_path(namespaces);
let function_call = quote! {
#namespace_path :: #nasp_pascal_ident :: #function_ident
};
let output: TokenStream2 = namespaces
.iter()
.enumerate()
.rev()
.fold(function_call, |acc, (index, nasp)| {
nasp_path_one_part(nasp, &acc, &namespaces, index)
});
output
}
}
}
fn nasp_path_one_part(
current_nasp: &Identifier,
input: &TokenStream2,
namespaces: &Vec<&Identifier>,
index: usize,
) -> TokenStream2 {
let namespaces_to_do = &namespaces[..index];
let ident_pascal = current_nasp.to_rust_pascalized();
if index == 0 {
quote! {
Commands :: #ident_pascal ( #input )
}
} else {
let ident_pascal_next = namespaces_to_do
.last()
.expect("We checked the index")
.to_rust_pascalized();
let namespace_path = Namespace::to_rust_path(namespaces_to_do);
quote! {
#namespace_path :: #ident_pascal_next :: #ident_pascal ( #input )
}
}
}