100 lines
3.5 KiB
Rust
100 lines
3.5 KiB
Rust
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use syn::{DeriveInput, Error, LitInt, LitStr, Result};
|
|
pub struct MessageAttr {
|
|
pub id: u16,
|
|
pub version: u16,
|
|
pub direction: TokenStream,
|
|
pub name: String,
|
|
}
|
|
pub fn expand_meta(input: &DeriveInput) -> Result<TokenStream> {
|
|
let attr = parse_message_attr(input)?;
|
|
let ident = &input.ident;
|
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
|
let id = attr.id;
|
|
let version = attr.version;
|
|
let direction = attr.direction;
|
|
let name = attr.name;
|
|
Ok(quote! {
|
|
impl #impl_generics ::titan::message::MessageMeta for #ident #ty_generics #where_clause {
|
|
const MESSAGE_TYPE: u16 = #id;
|
|
const MESSAGE_VERSION: u16 = #version;
|
|
const DIRECTION: ::titan::message::Direction = #direction;
|
|
const NAME: &'static str = #name;
|
|
}
|
|
})
|
|
}
|
|
fn parse_message_attr(input: &DeriveInput) -> Result<MessageAttr> {
|
|
let attr = input
|
|
.attrs
|
|
.iter()
|
|
.find(|attr| attr.path().is_ident("message"))
|
|
.ok_or_else(|| {
|
|
Error::new_spanned(
|
|
&input.ident,
|
|
"missing #[message(id = ..., direction = ...)] attribute",
|
|
)
|
|
})?;
|
|
let mut id: Option<u16> = None;
|
|
let mut version: u16 = 0;
|
|
let mut direction: Option<TokenStream> = None;
|
|
let mut name: Option<String> = None;
|
|
attr.parse_nested_meta(|meta| {
|
|
if meta.path.is_ident("id") {
|
|
let value: LitInt = meta.value()?.parse()?;
|
|
id = Some(value.base10_parse()?);
|
|
return Ok(());
|
|
}
|
|
if meta.path.is_ident("version") {
|
|
let value: LitInt = meta.value()?.parse()?;
|
|
version = value.base10_parse()?;
|
|
return Ok(());
|
|
}
|
|
if meta.path.is_ident("name") {
|
|
let value: LitStr = meta.value()?.parse()?;
|
|
name = Some(value.value());
|
|
return Ok(());
|
|
}
|
|
if meta.path.is_ident("direction") {
|
|
let value: LitStr = meta.value()?.parse()?;
|
|
direction = Some(match value.value().as_str() {
|
|
"client" | "client_to_server" => {
|
|
quote!(::titan::message::Direction::ClientToServer)
|
|
}
|
|
"server" | "server_to_client" => {
|
|
quote!(::titan::message::Direction::ServerToClient)
|
|
}
|
|
"both" | "bidirectional" => quote!(::titan::message::Direction::Bidirectional),
|
|
other => {
|
|
return Err(Error::new_spanned(
|
|
&value,
|
|
format!("unknown direction `{other}`, expected client, server or both"),
|
|
))
|
|
}
|
|
});
|
|
return Ok(());
|
|
}
|
|
Err(meta.error("unsupported #[message(...)] key"))
|
|
})?;
|
|
let id = id.ok_or_else(|| Error::new_spanned(attr, "#[message(...)] requires `id`"))?;
|
|
let direction = direction
|
|
.ok_or_else(|| Error::new_spanned(attr, "#[message(...)] requires `direction`"))?;
|
|
let name = name.unwrap_or_else(|| input.ident.to_string());
|
|
Ok(MessageAttr {
|
|
id,
|
|
version,
|
|
direction,
|
|
name,
|
|
})
|
|
}
|
|
pub fn expand_registration(input: &DeriveInput) -> TokenStream {
|
|
if !input.generics.params.is_empty() {
|
|
return TokenStream::new();
|
|
}
|
|
let ident = &input.ident;
|
|
quote! {
|
|
::titan::inventory::submit! {
|
|
::titan::factory::RegistryEntry::of::<#ident>()
|
|
}
|
|
}
|
|
}
|