skrillax_serde_derive/
lib.rs

1//! Generally it should be enough to simply `#[derive(Deserialize)]` or
2//! whichever trait you need. Just like the more general `serde` crate, this
3//! will handle most common things, like fields of different types, including
4//! references to other structures. However, there are a few things to
5//! keep in mind. Silkroad Online packets are not self-specifying, and thus we
6//! often need to provide just a little bit of help to serialize/deserialize
7//! some kinds of data. In general, you can provide additional options through
8//! the `#[silkroad]` tag. Which options are available for which elements will
9//! be explained in the following section.
10//!
11//! ## Enums
12//!
13//! Enums are generally serialized as one byte discriminant, followed by the
14//! content of that variant without further details. Currently, we don't
15//! automatically map the index of the enum variant to the discriminant. As
16//! such, you need to define a value manually. This can be done using
17//! `#[silkroad(value = 1)]` to set the variants byte value to `1`:
18//! ```
19//! #[derive(Serialize, Deserialize)]
20//! enum Hello {
21//!     #[silkroad(value = 1)]
22//!     ClientHello(String),
23//!     #[silkroad(value = 2)]
24//!     ServerHello(String),
25//! }
26//! ```
27//! In some cases it may be necessary for the discriminant to be two bytes wide,
28//! which you can specify using `#[silkroad(size = 2)]` on the enum itself:
29//! ```
30//! #[derive(Serialize, Deserialize)]
31//! #[silkroad(size = 2)]
32//! enum Hello {
33//!     #[silkroad(value = 0x400D)]
34//!     ClientHello(String),
35//! }
36//! ```
37//!
38//! ## Structs
39//!
40//! Structs are always serialized/deserialized by serializing/deserializing
41//! their fields. A unit struct therefor has length zero. There are also no
42//! options currently to alter the behavior for structs themselves, only their
43//! fields.
44//!
45//! ```
46//! #[derive(Serialize, Deserialize)]
47//! struct Hello(String);
48//! ```
49//!
50//! ## Fields
51//!
52//! The serialization/deserialization of fields is identical between structs and
53//! enums. Each field is serialized one after another without any separators.
54//! Therefor, it is necessary to match the size exactly to the consumed bytes.
55//! Fields are serialized and deserialized in the order they are defined.
56//!
57//! ```
58//! #[derive(Serialize, Deserialize)]
59//! struct Hello {
60//!     one_byte: u8,
61//!     two_bytes: u16,
62//! }
63//! ```
64//!
65//! ## Collections
66//!
67//! Collections (i.e. vectors) are encoded using one byte length followed by the
68//! elements of the collection without a separator. If the size is larger, this
69//! needs to be denoted using the `#[silkroad(size = 2)]` attribute.
70//! ```
71//! #[derive(Serialize, Deserialize)]
72//! struct Hello {
73//!     #[silkroad(size = 2)]
74//!     greetings: Vec<String>,
75//! }
76//! ```
77//! The default size is 1 with a size of up to 4 being supported.
78//! Additionally, you may change the type of encoding for a collection using the
79//! `list_type` attribute. This accepts one of three options: `length`
80//! (default), `break`, and `has-more`. `break` and `has-more` specify before
81//! each element if another element will follow using different values. `break`
82//! uses `1` for 'has more values' and `2` for finished, while `has-more`
83//! uses `1` for more elements and `0` for being finished.
84//! ```
85//! #[derive(Serialize, Deserialize)]
86//! struct Hello {
87//!     #[silkroad(list_type = "break")]
88//!     greetings: Vec<String>,
89//! }
90//! ```
91//!
92//! ## Strings
93//!
94//! Generally a string is encoded using two bytes length and then the UTF-8
95//! representation of that string. In some cases, Silkroad however uses two byte
96//! wide characters (UTF-16) in strings. This can be configured by using a
97//! `size` of 2.
98//! ```
99//! #[derive(Serialize, Deserialize)]
100//! struct Hello {
101//!     #[silkroad(size = 2)]
102//!     greeting: String,
103//! }
104//! ```
105//!
106//! ## Optional
107//!
108//! Optional values will be encoded using a byte denoting the presence (1) or
109//! absence (0), following the underlying value if it is present. In some cases,
110//! due to previous knowledge, optional values may just appear (or be missing)
111//! without the presence indicator. This makes them impossible to deserialize
112//! (currently), but this is unfortunately current necessary. To achieve this,
113//! you can set the size of the field to 0.
114//! ```rust
115//! #[derive(Serialize)]
116//! struct Hello {
117//!     #[silkroad(size = 0)]
118//!     greeting: Option<String>,
119//! }
120//! ```
121//!
122//! Alternatively, if there is an indication in the data whether the value will
123//! be present or not, you can use the `when` attribute to specify a condition.
124//! In that case the presence byte will be omitted as well, but makes it
125//! possible to be deserialized. This does not make any checks for serialization
126//! and will always append a present value, ignoring the condition. The
127//! condition in `when` should denote an expression which returns a boolean,
128//! showing if the values is present in the packet or not. It is possible to
129//! access any previous values, but is currently limited to expressions without
130//! imports.
131//! ```rust
132//! #[derive(Deserialize, Serialize)]
133//! struct Hello {
134//!     condition: u8
135//!     #[silkroad(when = "condition == 1")]
136//!     greeting: Option<String>
137//! }
138//! ```
139
140use crate::deserialize::deserialize;
141use crate::serialize::serialize;
142use crate::size::size;
143use darling::{FromAttributes, FromDeriveInput};
144use proc_macro::TokenStream;
145use proc_macro_error::{abort, proc_macro_error};
146use quote::{quote, ToTokens};
147use syn::spanned::Spanned;
148use syn::{parse_macro_input, DeriveInput, Expr, GenericArgument, PathArguments, Type};
149
150mod deserialize;
151mod serialize;
152mod size;
153
154pub(crate) const DEFAULT_LIST_TYPE: &str = "length";
155
156#[derive(FromAttributes)]
157#[darling(attributes(silkroad))]
158pub(crate) struct FieldArgs {
159    list_type: Option<String>,
160    size: Option<usize>,
161    value: Option<usize>,
162    when: Option<String>,
163}
164
165#[derive(FromDeriveInput)]
166#[darling(attributes(silkroad))]
167pub(crate) struct SilkroadArgs {
168    size: Option<usize>,
169}
170
171#[proc_macro_error]
172#[proc_macro_derive(Serialize, attributes(silkroad))]
173pub fn derive_serialize(input: TokenStream) -> TokenStream {
174    let input = parse_macro_input!(input);
175    let args = SilkroadArgs::from_derive_input(&input).unwrap();
176    let DeriveInput { ident, data, .. } = input;
177
178    let output = serialize(&ident, &data, args);
179
180    let output = quote! {
181        impl skrillax_serde::Serialize for #ident {
182            fn write_to(&self, mut writer: &mut ::skrillax_serde::__internal::bytes::BytesMut) {
183                #output
184            }
185        }
186
187        impl From<#ident> for ::skrillax_serde::__internal::bytes::Bytes {
188            fn from(packet: #ident) -> ::skrillax_serde::__internal::bytes::Bytes {
189                let mut buffer = ::skrillax_serde::__internal::bytes::BytesMut::with_capacity(packet.byte_size());
190                packet.write_to(&mut buffer);
191                buffer.freeze()
192            }
193        }
194    };
195    output.into()
196}
197
198#[proc_macro_error]
199#[proc_macro_derive(Deserialize, attributes(silkroad))]
200pub fn derive_deserialize(input: TokenStream) -> TokenStream {
201    let input = parse_macro_input!(input);
202    let args = SilkroadArgs::from_derive_input(&input).unwrap();
203    let DeriveInput { ident, data, .. } = input;
204    let output = deserialize(&ident, &data, args);
205    let output = quote! {
206        impl skrillax_serde::Deserialize for #ident {
207            fn read_from<T: std::io::Read + ::skrillax_serde::__internal::byteorder::ReadBytesExt>(mut reader: &mut T) -> Result<Self, skrillax_serde::SerializationError> {
208                #output
209            }
210        }
211
212        impl TryFrom<::skrillax_serde::__internal::bytes::Bytes> for #ident {
213            type Error = skrillax_serde::SerializationError;
214
215            fn try_from(data: ::skrillax_serde::__internal::bytes::Bytes) -> Result<Self, Self::Error> {
216                use ::skrillax_serde::__internal::bytes::Buf;
217                let mut data_reader = data.reader();
218                #ident::read_from(&mut data_reader)
219            }
220        }
221    };
222    output.into()
223}
224
225#[proc_macro_error]
226#[proc_macro_derive(ByteSize, attributes(silkroad))]
227pub fn derive_size(input: TokenStream) -> TokenStream {
228    let input = parse_macro_input!(input);
229    let args = SilkroadArgs::from_derive_input(&input).unwrap();
230    let DeriveInput { ident, data, .. } = input;
231    let output = size(&ident, &data, args);
232    let output = quote! {
233        impl skrillax_serde::ByteSize for #ident {
234            fn byte_size(&self) -> usize {
235                #output
236            }
237        }
238    };
239    output.into()
240}
241
242#[derive(Debug)]
243pub(crate) enum UsedType<'a> {
244    Primitive,
245    String,
246    Array(&'a Expr),
247    Collection(&'a Type),
248    Option(&'a Type),
249    Tuple(Vec<&'a Type>),
250}
251
252pub(crate) fn get_type_of(ty: &Type) -> UsedType {
253    match ty {
254        Type::Array(arr) => UsedType::Array(&arr.len),
255        Type::Reference(_) => abort!(ty, "References are not supported for (de)serialization."),
256        Type::Tuple(tuple) => UsedType::Tuple(tuple.elems.iter().collect()),
257        Type::Path(path) => {
258            let full_name = path
259                .path
260                .segments
261                .iter()
262                .map(|segment| segment.ident.to_string())
263                .collect::<Vec<String>>()
264                .join("::");
265
266            if full_name == "String"
267                || full_name == "string::String"
268                || full_name == "std::string::String"
269            {
270                return UsedType::String;
271            } else if full_name == "Vec" {
272                match path.path.segments.last().unwrap().arguments {
273                    PathArguments::None => {
274                        abort!(ty, "Missing generic parameters for collection type.")
275                    },
276                    PathArguments::Parenthesized(_) => {
277                        abort!(ty, "Cannot use parenthesized types.")
278                    },
279                    PathArguments::AngleBracketed(ref args) => {
280                        let ty = args
281                            .args
282                            .iter()
283                            .find_map(|arg| match arg {
284                                GenericArgument::Type(ty) => Some(ty),
285                                _ => None,
286                            })
287                            .unwrap();
288                        return UsedType::Collection(ty);
289                    },
290                }
291            } else if full_name == "Option" {
292                match path.path.segments.last().unwrap().arguments {
293                    PathArguments::None => {
294                        abort!(ty, "Missing generic parameters for option type.")
295                    },
296                    PathArguments::Parenthesized(_) => {
297                        abort!(ty, "Cannot use parenthesized types.")
298                    },
299                    PathArguments::AngleBracketed(ref args) => {
300                        let ty = args
301                            .args
302                            .iter()
303                            .find_map(|arg| match arg {
304                                GenericArgument::Type(ty) => Some(ty),
305                                _ => None,
306                            })
307                            .unwrap();
308                        return UsedType::Option(ty);
309                    },
310                }
311            }
312
313            UsedType::Primitive
314        },
315        _ => abort!(ty, "Encountered unknown syn type."),
316    }
317}
318
319fn get_variant_value<T: Spanned + ToTokens>(source: &T, value: usize, size: usize) -> Expr {
320    let ty = match size {
321        1 => "u8",
322        2 => "u16",
323        4 => "u32",
324        8 => "u64",
325        _ => abort!(source, "Unknown size"),
326    };
327    syn::parse_str(&format!("{}{}", value, ty)).unwrap()
328}