rust-nex/macros/src/lib.rs

163 lines
4.9 KiB
Rust
Raw Normal View History

2026-05-04 16:06:25 +02:00
#![allow(dead_code)]
2025-03-23 10:54:01 +01:00
mod protos;
2026-05-04 16:06:25 +02:00
mod rmc_struct;
mod util;
2025-03-23 10:54:01 +01:00
extern crate proc_macro;
2026-05-04 16:06:25 +02:00
use crate::protos::{ProtoInputParams, RmcProtocolData};
use crate::rmc_struct::{rmc_serialize_enum, rmc_serialize_struct};
use proc_macro::TokenStream;
2026-05-04 16:06:25 +02:00
use proc_macro2::Ident;
use quote::quote;
use syn::spanned::Spanned;
2026-05-04 16:06:25 +02:00
use syn::{parse_macro_input, Data, DeriveInput, Lit, LitStr};
2025-03-08 00:56:44 +01:00
2026-05-04 16:06:25 +02:00
#[proc_macro_derive(RmcSerialize, attributes(extends, rmc_struct))]
pub fn rmc_serialize(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
2026-05-04 16:06:25 +02:00
let (serialize, deserialize, write_size, version) = match &derive_input.data {
Data::Struct(s) => rmc_serialize_struct(s, &derive_input),
Data::Enum(e) => rmc_serialize_enum(e, &derive_input),
Data::Union(_) => {
unimplemented!("serialize a union is not allowed");
}
};
2026-05-04 16:06:25 +02:00
// generate base data
2026-05-04 16:06:25 +02:00
let str_name = Lit::Str(LitStr::new(
&derive_input.ident.to_string(),
derive_input.ident.span(),
));
let ident = derive_input.ident;
2026-05-04 16:06:25 +02:00
let write_size = if let Some(v) = write_size {
2025-06-29 11:40:42 +02:00
quote! {
2026-05-04 16:06:25 +02:00
fn serialize_write_size(&self) -> rnex_core::rmc::structures::Result<u32>{
#v
}
}
2025-11-12 22:41:34 +01:00
} else {
2026-05-04 16:06:25 +02:00
quote! {}
2025-11-12 22:41:34 +01:00
};
2026-05-04 16:06:25 +02:00
let version = if let Some(v) = version {
quote! {
2026-05-04 16:06:25 +02:00
fn version() -> Option<u8>{
#v
}
}
} else {
2026-05-04 16:06:25 +02:00
quote! {}
2025-06-29 11:40:42 +02:00
};
let tokens = quote! {
2025-09-21 15:59:27 +02:00
impl rnex_core::rmc::structures::RmcSerialize for #ident{
2025-11-12 22:41:34 +01:00
#[inline(always)]
fn serialize(&self, writer: &mut impl ::std::io::Write) -> rnex_core::rmc::structures::Result<()>{
2026-05-04 16:06:25 +02:00
#serialize
}
2025-11-12 22:41:34 +01:00
#[inline(always)]
fn deserialize(reader: &mut impl ::std::io::Read) -> rnex_core::rmc::structures::Result<Self>{
2026-05-04 16:06:25 +02:00
#deserialize
}
2025-11-12 22:41:34 +01:00
#write_size
2026-04-25 14:24:33 +02:00
2026-05-04 16:06:25 +02:00
#version
2026-04-25 14:24:33 +02:00
fn name() -> &'static str{
#str_name
}
}
};
tokens.into()
}
2025-03-23 10:54:01 +01:00
/// Macro to automatically generate code to use a specific trait as an rmc protocol for calling to
/// remote objects or accepting incoming remote requests.
/// This is needed in order to be able to use this as part of an rmc server interface.
///
/// The protocol id which is needed to be specified is specified as a parameter to this attribute.
///
/// You will also need to assign each function inside the trait a method id by using the
/// [`macro@method_id`] attribute.
///
/// You can also specify to have the protocol to be non-returning by adding a second parameter to
2025-06-29 11:40:42 +02:00
/// the attribute which is just `NoReturn` e.g. `#[rmc_proto(1, NoReturn)]`
2025-03-23 10:54:01 +01:00
///
/// Example
/// ```
/// // this rmc protocol has protocol id 1
/// use macros::rmc_proto;
///
/// #[rmc_proto(1)]
/// trait ExampleProtocol{
/// // this defines an rmc method with id 1
/// #[rmc_method(1)]
/// async fn hello_world_method(&self, name: String) -> Result<String, ErrorCode>;
/// }
/// ```
#[proc_macro_attribute]
2025-06-29 11:40:42 +02:00
pub fn rmc_proto(attr: TokenStream, input: TokenStream) -> TokenStream {
let params = parse_macro_input!(attr as ProtoInputParams);
let input = parse_macro_input!(input as syn::ItemTrait);
2026-05-04 16:06:25 +02:00
let raw_data = RmcProtocolData::new(params, &input);
2025-03-08 00:56:44 +01:00
2025-06-29 11:40:42 +02:00
quote! {
#input
2025-03-23 10:54:01 +01:00
#raw_data
2025-06-29 11:40:42 +02:00
}
.into()
}
2025-03-23 10:54:01 +01:00
/// Used to specify the method id of methods when making rmc protocols.
/// See [`macro@rmc_proto`] for further details.
///
/// Note: This attribute doesn't do anything by itself and just returns the thing it was attached to
/// unchanged.
#[proc_macro_attribute]
2025-06-29 11:40:42 +02:00
pub fn method_id(_attr: TokenStream, input: TokenStream) -> TokenStream {
// this attribute doesnt do anything by itself, see `rmc_proto`
input
}
#[proc_macro_attribute]
2025-06-29 11:40:42 +02:00
pub fn rmc_struct(attr: TokenStream, input: TokenStream) -> TokenStream {
let type_data = parse_macro_input!(input as DeriveInput);
let mut ident = parse_macro_input!(attr as syn::Path);
let last_token = ident.segments.last_mut().expect("empty path?");
2025-06-29 11:40:42 +02:00
last_token.ident = Ident::new(
&("Local".to_owned() + &last_token.ident.to_string()),
last_token.span(),
);
let struct_name = &type_data.ident;
2025-06-29 11:40:42 +02:00
let out = quote! {
#type_data
impl #ident for #struct_name{
}
2025-09-21 15:59:27 +02:00
impl rnex_core::rmc::protocols::RmcCallable for #struct_name{
async fn rmc_call(&self, remote_response_connection: &rnex_core::util::SendingBufferConnection, protocol_id: u16, method_id: u32, call_id: u32, rest: Vec<u8>){
2025-03-23 10:54:01 +01:00
<Self as #ident>::rmc_call(self, remote_response_connection, protocol_id, method_id, call_id, rest).await;
}
}
};
out.into()
2025-03-08 00:56:44 +01:00
}
#[proc_macro_attribute]
2025-06-29 11:40:42 +02:00
pub fn connection(_attr: TokenStream, input: TokenStream) -> TokenStream {
2025-03-08 00:56:44 +01:00
// this attribute doesnt do anything by itself, see `rmc_struct`
input
2025-06-29 11:40:42 +02:00
}