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}