valence_protocol_macros/
packet.rs
1use heck::ToShoutySnakeCase;
2use proc_macro2::{Ident, Span, TokenStream};
3use quote::quote;
4use syn::spanned::Spanned;
5use syn::{parse2, parse_quote, Attribute, DeriveInput, Error, Expr, LitInt, LitStr, Result};
6
7use crate::add_trait_bounds;
8
9pub(super) fn derive_packet(item: TokenStream) -> Result<TokenStream> {
10 let mut input = parse2::<DeriveInput>(item)?;
11
12 let packet_attr = parse_packet_helper_attr(&input.attrs)?.unwrap_or_default();
13
14 let name = input.ident.clone();
15
16 let name_str = if let Some(attr_name) = packet_attr.name {
17 attr_name.value()
18 } else {
19 name.to_string()
20 };
21
22 let packet_id: Expr = match packet_attr.id {
23 Some(expr) => expr,
24 None => match syn::parse_str::<Ident>(&name_str.to_shouty_snake_case()) {
25 Ok(ident) => parse_quote!(::valence_protocol::packet_id::#ident),
26 Err(_) => {
27 return Err(Error::new(
28 packet_attr.span,
29 "missing valid `id = ...` value from `packet` attr",
30 ))
31 }
32 },
33 };
34
35 add_trait_bounds(&mut input.generics, quote!(::std::fmt::Debug));
36
37 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
38
39 let side = if let Some(side_attr) = packet_attr.side {
40 side_attr
41 } else if name_str.to_lowercase().ends_with("s2c") {
42 parse_quote!(::valence_protocol::PacketSide::Clientbound)
43 } else if name_str.to_lowercase().ends_with("c2s") {
44 parse_quote!(::valence_protocol::PacketSide::Serverbound)
45 } else {
46 return Err(Error::new(
47 packet_attr.span,
48 "missing `side = PacketSide::...` value from `packet` attribute",
49 ));
50 };
51
52 let state = packet_attr
53 .state
54 .unwrap_or_else(|| parse_quote!(::valence_protocol::PacketState::Play));
55
56 Ok(quote! {
57 impl #impl_generics ::valence_protocol::__private::Packet for #name #ty_generics
58 #where_clause
59 {
60 const ID: i32 = #packet_id;
61 const NAME: &'static str = #name_str;
62 const SIDE: ::valence_protocol::PacketSide = #side;
63 const STATE: ::valence_protocol::PacketState = #state;
64 }
65 })
66}
67
68struct PacketAttr {
69 span: Span,
70 id: Option<Expr>,
71 tag: Option<i32>,
72 name: Option<LitStr>,
73 side: Option<Expr>,
74 state: Option<Expr>,
75}
76
77impl Default for PacketAttr {
78 fn default() -> Self {
79 Self {
80 span: Span::call_site(),
81 id: Default::default(),
82 tag: Default::default(),
83 name: Default::default(),
84 side: Default::default(),
85 state: Default::default(),
86 }
87 }
88}
89
90fn parse_packet_helper_attr(attrs: &[Attribute]) -> Result<Option<PacketAttr>> {
91 for attr in attrs {
92 if attr.path().is_ident("packet") {
93 let mut res = PacketAttr {
94 span: attr.span(),
95 id: None,
96 tag: None,
97 name: None,
98 side: None,
99 state: None,
100 };
101
102 attr.parse_nested_meta(|meta| {
103 if meta.path.is_ident("id") {
104 res.id = Some(meta.value()?.parse::<Expr>()?);
105 Ok(())
106 } else if meta.path.is_ident("tag") {
107 res.tag = Some(meta.value()?.parse::<LitInt>()?.base10_parse::<i32>()?);
108 Ok(())
109 } else if meta.path.is_ident("name") {
110 res.name = Some(meta.value()?.parse::<LitStr>()?);
111 Ok(())
112 } else if meta.path.is_ident("side") {
113 res.side = Some(meta.value()?.parse::<Expr>()?);
114 Ok(())
115 } else if meta.path.is_ident("state") {
116 res.state = Some(meta.value()?.parse::<Expr>()?);
117 Ok(())
118 } else {
119 Err(meta.error("unrecognized packet argument"))
120 }
121 })?;
122
123 return Ok(Some(res));
124 }
125 }
126
127 Ok(None)
128}