1 | use std::fmt::Write; |
2 | |
3 | use proc_macro2::{Ident, Literal, Span, TokenStream}; |
4 | |
5 | use quote::{format_ident, quote, ToTokens}; |
6 | |
7 | use crate::{protocol::*, util::*, Side}; |
8 | |
9 | pub(crate) fn generate_enums_for(interface: &Interface) -> TokenStream { |
10 | interface.enums.iter().map(ToTokens::into_token_stream).collect() |
11 | } |
12 | |
13 | impl ToTokens for Enum { |
14 | fn to_tokens(&self, tokens: &mut TokenStream) { |
15 | let enum_decl; |
16 | let enum_impl; |
17 | |
18 | let doc_attr = self.description.as_ref().map(description_to_doc_attr); |
19 | let ident = Ident::new(&snake_to_camel(&self.name), Span::call_site()); |
20 | |
21 | if self.bitfield { |
22 | let entries = self.entries.iter().map(|entry| { |
23 | let doc_attr = entry |
24 | .description |
25 | .as_ref() |
26 | .map(description_to_doc_attr) |
27 | .or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s))); |
28 | |
29 | let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" }; |
30 | let ident = format_ident!(" {}{}" , prefix, snake_to_camel(&entry.name)); |
31 | |
32 | let value = Literal::u32_unsuffixed(entry.value); |
33 | |
34 | quote! { |
35 | #doc_attr |
36 | const #ident = #value; |
37 | } |
38 | }); |
39 | |
40 | enum_decl = quote! { |
41 | bitflags::bitflags! { |
42 | #doc_attr |
43 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
44 | pub struct #ident: u32 { |
45 | #(#entries)* |
46 | } |
47 | } |
48 | }; |
49 | enum_impl = quote! { |
50 | impl std::convert::TryFrom<u32> for #ident { |
51 | type Error = (); |
52 | fn try_from(val: u32) -> Result<#ident, ()> { |
53 | #ident::from_bits(val).ok_or(()) |
54 | } |
55 | } |
56 | impl std::convert::From<#ident> for u32 { |
57 | fn from(val: #ident) -> u32 { |
58 | val.bits() |
59 | } |
60 | } |
61 | }; |
62 | } else { |
63 | let variants = self.entries.iter().map(|entry| { |
64 | let doc_attr = entry |
65 | .description |
66 | .as_ref() |
67 | .map(description_to_doc_attr) |
68 | .or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s))); |
69 | |
70 | let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" }; |
71 | let variant = format_ident!(" {}{}" , prefix, snake_to_camel(&entry.name)); |
72 | |
73 | let value = Literal::u32_unsuffixed(entry.value); |
74 | |
75 | quote! { |
76 | #doc_attr |
77 | #variant = #value |
78 | } |
79 | }); |
80 | |
81 | enum_decl = quote! { |
82 | #doc_attr |
83 | #[repr(u32)] |
84 | #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] |
85 | #[non_exhaustive] |
86 | pub enum #ident { |
87 | #(#variants,)* |
88 | } |
89 | }; |
90 | |
91 | let match_arms = self.entries.iter().map(|entry| { |
92 | let value = Literal::u32_unsuffixed(entry.value); |
93 | |
94 | let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" }; |
95 | let variant = format_ident!(" {}{}" , prefix, snake_to_camel(&entry.name)); |
96 | |
97 | quote! { |
98 | #value => Ok(#ident::#variant) |
99 | } |
100 | }); |
101 | |
102 | enum_impl = quote! { |
103 | impl std::convert::TryFrom<u32> for #ident { |
104 | type Error = (); |
105 | fn try_from(val: u32) -> Result<#ident, ()> { |
106 | match val { |
107 | #(#match_arms,)* |
108 | _ => Err(()) |
109 | } |
110 | } |
111 | } |
112 | impl std::convert::From<#ident> for u32 { |
113 | fn from(val: #ident) -> u32 { |
114 | val as u32 |
115 | } |
116 | } |
117 | }; |
118 | } |
119 | |
120 | enum_decl.to_tokens(tokens); |
121 | enum_impl.to_tokens(tokens); |
122 | } |
123 | } |
124 | |
125 | pub(crate) fn gen_msg_constants(requests: &[Message], events: &[Message]) -> TokenStream { |
126 | let req_constants = requests.iter().enumerate().map(|(opcode, msg)| { |
127 | let since_cstname = format_ident!("REQ_ {}_SINCE" , msg.name.to_ascii_uppercase()); |
128 | let opcode_cstname = format_ident!("REQ_ {}_OPCODE" , msg.name.to_ascii_uppercase()); |
129 | let since = msg.since; |
130 | let opcode = opcode as u16; |
131 | quote! { |
132 | /// The minimal object version supporting this request |
133 | pub const #since_cstname: u32 = #since; |
134 | /// The wire opcode for this request |
135 | pub const #opcode_cstname: u16 = #opcode; |
136 | } |
137 | }); |
138 | let evt_constants = events.iter().enumerate().map(|(opcode, msg)| { |
139 | let since_cstname = format_ident!("EVT_ {}_SINCE" , msg.name.to_ascii_uppercase()); |
140 | let opcode_cstname = format_ident!("EVT_ {}_OPCODE" , msg.name.to_ascii_uppercase()); |
141 | let since = msg.since; |
142 | let opcode = opcode as u16; |
143 | quote! { |
144 | /// The minimal object version supporting this event |
145 | pub const #since_cstname: u32 = #since; |
146 | /// The wire opcode for this event |
147 | pub const #opcode_cstname: u16 = #opcode; |
148 | } |
149 | }); |
150 | |
151 | quote! { |
152 | #(#req_constants)* |
153 | #(#evt_constants)* |
154 | } |
155 | } |
156 | |
157 | pub(crate) fn gen_message_enum( |
158 | name: &Ident, |
159 | side: Side, |
160 | receiver: bool, |
161 | messages: &[Message], |
162 | ) -> TokenStream { |
163 | let variants = messages |
164 | .iter() |
165 | .map(|msg| { |
166 | let mut docs = String::new(); |
167 | if let Some((ref short, ref long)) = msg.description { |
168 | write!(docs, " {}\n\n{}\n" , short, long.trim()).unwrap(); |
169 | } |
170 | if let Some(Type::Destructor) = msg.typ { |
171 | write!( |
172 | docs, |
173 | " \nThis is a destructor, once {} this object cannot be used any longer." , |
174 | if receiver { "received" } else { "sent" }, |
175 | ) |
176 | .unwrap() |
177 | } |
178 | if msg.since > 1 { |
179 | write!(docs, " \nOnly available since version {} of the interface" , msg.since) |
180 | .unwrap(); |
181 | } |
182 | |
183 | let doc_attr = to_doc_attr(&docs); |
184 | let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); |
185 | let msg_variant_decl = |
186 | if msg.args.is_empty() { |
187 | msg_name.into_token_stream() |
188 | } else { |
189 | let fields = msg.args.iter().flat_map(|arg| { |
190 | let field_name = |
191 | format_ident!(" {}{}" , if is_keyword(&arg.name) { "_" } else { "" }, arg.name); |
192 | let field_type_inner = if let Some(ref enu) = arg.enum_ { |
193 | let enum_type = dotted_to_relname(enu); |
194 | quote! { WEnum<#enum_type> } |
195 | } else { |
196 | match arg.typ { |
197 | Type::Uint => quote! { u32 }, |
198 | Type::Int => quote! { i32 }, |
199 | Type::Fixed => quote! { f64 }, |
200 | Type::String => quote! { String }, |
201 | Type::Array => quote! { Vec<u8> }, |
202 | Type::Fd => { |
203 | if receiver { |
204 | quote! { OwnedFd } |
205 | } else { |
206 | quote! { std::os::unix::io::BorrowedFd<'a> } |
207 | } |
208 | } |
209 | Type::Object => { |
210 | if let Some(ref iface) = arg.interface { |
211 | let iface_mod = Ident::new(iface, Span::call_site()); |
212 | let iface_type = |
213 | Ident::new(&snake_to_camel(iface), Span::call_site()); |
214 | quote! { super::#iface_mod::#iface_type } |
215 | } else if side == Side::Client { |
216 | quote! { super::wayland_client::ObjectId } |
217 | } else { |
218 | quote! { super::wayland_server::ObjectId } |
219 | } |
220 | } |
221 | Type::NewId if !receiver && side == Side::Client => { |
222 | // Client-side sending does not have a pre-existing object |
223 | // so skip serializing it |
224 | if arg.interface.is_some() { |
225 | return None; |
226 | } else { |
227 | quote! { (&'static Interface, u32) } |
228 | } |
229 | } |
230 | Type::NewId => { |
231 | if let Some(ref iface) = arg.interface { |
232 | let iface_mod = Ident::new(iface, Span::call_site()); |
233 | let iface_type = |
234 | Ident::new(&snake_to_camel(iface), Span::call_site()); |
235 | if receiver && side == Side::Server { |
236 | quote! { New<super::#iface_mod::#iface_type> } |
237 | } else { |
238 | quote! { super::#iface_mod::#iface_type } |
239 | } |
240 | } else { |
241 | // bind-like function |
242 | if side == Side::Client { |
243 | quote! { (String, u32, super::wayland_client::ObjectId) } |
244 | } else { |
245 | quote! { (String, u32, super::wayland_server::ObjectId) } |
246 | } |
247 | } |
248 | } |
249 | Type::Destructor => panic!("An argument cannot have type \"destructor \"." ), |
250 | } |
251 | }; |
252 | |
253 | let field_type = if arg.allow_null { |
254 | quote! { Option<#field_type_inner> } |
255 | } else { |
256 | field_type_inner.into_token_stream() |
257 | }; |
258 | |
259 | let doc_attr = arg |
260 | .description |
261 | .as_ref() |
262 | .map(description_to_doc_attr) |
263 | .or_else(|| arg.summary.as_ref().map(|s| to_doc_attr(s))); |
264 | |
265 | Some(quote! { |
266 | #doc_attr |
267 | #field_name: #field_type |
268 | }) |
269 | }); |
270 | |
271 | quote! { |
272 | #msg_name { |
273 | #(#fields,)* |
274 | } |
275 | } |
276 | }; |
277 | |
278 | quote! { |
279 | #doc_attr |
280 | #msg_variant_decl |
281 | } |
282 | }) |
283 | .collect::<Vec<_>>(); |
284 | |
285 | let opcodes = messages.iter().enumerate().map(|(opcode, msg)| { |
286 | let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); |
287 | let opcode = opcode as u16; |
288 | if msg.args.is_empty() { |
289 | quote! { |
290 | #name::#msg_name => #opcode |
291 | } |
292 | } else { |
293 | quote! { |
294 | #name::#msg_name { .. } => #opcode |
295 | } |
296 | } |
297 | }); |
298 | |
299 | // Placeholder to allow generic argument to be added later, without ABI |
300 | // break. |
301 | // TODO Use never type. |
302 | let (generic, phantom_variant, phantom_case) = if !receiver { |
303 | ( |
304 | quote! { 'a }, |
305 | quote! { #[doc(hidden)] __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()>, never: std::convert::Infallible } }, |
306 | quote! { #name::__phantom_lifetime { never, .. } => match never {} }, |
307 | ) |
308 | } else { |
309 | (quote! {}, quote! {}, quote! {}) |
310 | }; |
311 | |
312 | quote! { |
313 | #[derive(Debug)] |
314 | #[non_exhaustive] |
315 | pub enum #name<#generic> { |
316 | #(#variants,)* |
317 | #phantom_variant |
318 | } |
319 | |
320 | impl<#generic> #name<#generic> { |
321 | #[doc="Get the opcode number of this message" ] |
322 | pub fn opcode(&self) -> u16 { |
323 | match *self { |
324 | #(#opcodes,)* |
325 | #phantom_case |
326 | } |
327 | } |
328 | } |
329 | } |
330 | } |
331 | |
332 | pub(crate) fn gen_parse_body(interface: &Interface, side: Side) -> TokenStream { |
333 | let msgs = match side { |
334 | Side::Client => &interface.events, |
335 | Side::Server => &interface.requests, |
336 | }; |
337 | let object_type = Ident::new( |
338 | match side { |
339 | Side::Client => "Proxy" , |
340 | Side::Server => "Resource" , |
341 | }, |
342 | Span::call_site(), |
343 | ); |
344 | let msg_type = Ident::new( |
345 | match side { |
346 | Side::Client => "Event" , |
347 | Side::Server => "Request" , |
348 | }, |
349 | Span::call_site(), |
350 | ); |
351 | |
352 | let match_arms = msgs.iter().enumerate().map(|(opcode, msg)| { |
353 | let opcode = opcode as u16; |
354 | let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); |
355 | let args_pat = msg.args.iter().map(|arg| { |
356 | let arg_name = Ident::new( |
357 | &format!(" {}{}" , if is_keyword(&arg.name) { "_" } else { "" }, arg.name), |
358 | Span::call_site(), |
359 | ); |
360 | match arg.typ { |
361 | Type::Uint => quote!{ Some(Argument::Uint(#arg_name)) }, |
362 | Type::Int => quote!{ Some(Argument::Int(#arg_name)) }, |
363 | Type::String => quote!{ Some(Argument::Str(#arg_name)) }, |
364 | Type::Fixed => quote!{ Some(Argument::Fixed(#arg_name)) }, |
365 | Type::Array => quote!{ Some(Argument::Array(#arg_name)) }, |
366 | Type::Object => quote!{ Some(Argument::Object(#arg_name)) }, |
367 | Type::NewId => quote!{ Some(Argument::NewId(#arg_name)) }, |
368 | Type::Fd => quote!{ Some(Argument::Fd(#arg_name)) }, |
369 | Type::Destructor => panic!("Argument {}. {}. {} has type destructor ?!" , interface.name, msg.name, arg.name), |
370 | } |
371 | }); |
372 | |
373 | let args_iter = msg.args.iter().map(|_| quote!{ arg_iter.next() }); |
374 | |
375 | let arg_names = msg.args.iter().map(|arg| { |
376 | let arg_name = format_ident!(" {}{}" , if is_keyword(&arg.name) { "_" } else { "" }, arg.name); |
377 | if arg.enum_.is_some() { |
378 | quote! { #arg_name: From::from(#arg_name as u32) } |
379 | } else { |
380 | match arg.typ { |
381 | Type::Uint | Type::Int | Type::Fd => quote!{ #arg_name }, |
382 | Type::Fixed => quote!{ #arg_name: (#arg_name as f64) / 256.}, |
383 | Type::String => { |
384 | if arg.allow_null { |
385 | quote! { |
386 | #arg_name: #arg_name.as_ref().map(|s| String::from_utf8_lossy(s.as_bytes()).into_owned()) |
387 | } |
388 | } else { |
389 | quote! { |
390 | #arg_name: String::from_utf8_lossy(#arg_name.as_ref().unwrap().as_bytes()).into_owned() |
391 | } |
392 | } |
393 | }, |
394 | Type::Object => { |
395 | let create_proxy = if let Some(ref created_interface) = arg.interface { |
396 | let created_iface_mod = Ident::new(created_interface, Span::call_site()); |
397 | let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site()); |
398 | quote! { |
399 | match <super::#created_iface_mod::#created_iface_type as #object_type>::from_id(conn, #arg_name.clone()) { |
400 | Ok(p) => p, |
401 | Err(_) => return Err(DispatchError::BadMessage { |
402 | sender_id: msg.sender_id, |
403 | interface: Self::interface().name, |
404 | opcode: msg.opcode |
405 | }), |
406 | } |
407 | } |
408 | } else { |
409 | quote! { #arg_name.clone() } |
410 | }; |
411 | if arg.allow_null { |
412 | quote! { |
413 | #arg_name: if #arg_name.is_null() { None } else { Some(#create_proxy) } |
414 | } |
415 | } else { |
416 | quote! { |
417 | #arg_name: #create_proxy |
418 | } |
419 | } |
420 | }, |
421 | Type::NewId => { |
422 | let create_proxy = if let Some(ref created_interface) = arg.interface { |
423 | let created_iface_mod = Ident::new(created_interface, Span::call_site()); |
424 | let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site()); |
425 | quote! { |
426 | match <super::#created_iface_mod::#created_iface_type as #object_type>::from_id(conn, #arg_name.clone()) { |
427 | Ok(p) => p, |
428 | Err(_) => return Err(DispatchError::BadMessage { |
429 | sender_id: msg.sender_id, |
430 | interface: Self::interface().name, |
431 | opcode: msg.opcode, |
432 | }), |
433 | } |
434 | } |
435 | } else if side == Side::Server { |
436 | quote! { New::wrap(#arg_name.clone()) } |
437 | } else { |
438 | quote! { #arg_name.clone() } |
439 | }; |
440 | if arg.allow_null { |
441 | if side == Side::Server { |
442 | quote! { |
443 | #arg_name: if #arg_name.is_null() { None } else { Some(New::wrap(#create_proxy)) } |
444 | } |
445 | } else { |
446 | quote! { |
447 | #arg_name: if #arg_name.is_null() { None } else { Some(#create_proxy) } |
448 | } |
449 | } |
450 | } else if side == Side::Server { |
451 | quote! { |
452 | #arg_name: New::wrap(#create_proxy) |
453 | } |
454 | } else { |
455 | quote! { |
456 | #arg_name: #create_proxy |
457 | } |
458 | } |
459 | }, |
460 | Type::Array => { |
461 | if arg.allow_null { |
462 | quote! { if #arg_name.len() == 0 { None } else { Some(*#arg_name) } } |
463 | } else { |
464 | quote! { #arg_name: *#arg_name } |
465 | } |
466 | }, |
467 | Type::Destructor => unreachable!(), |
468 | } |
469 | } |
470 | }); |
471 | |
472 | quote! { |
473 | #opcode => { |
474 | if let (#(#args_pat),*) = (#(#args_iter),*) { |
475 | Ok((me, #msg_type::#msg_name { #(#arg_names),* })) |
476 | } else { |
477 | Err(DispatchError::BadMessage { sender_id: msg.sender_id, interface: Self::interface().name, opcode: msg.opcode }) |
478 | } |
479 | } |
480 | } |
481 | }); |
482 | |
483 | quote! { |
484 | let me = Self::from_id(conn, msg.sender_id.clone()).unwrap(); |
485 | let mut arg_iter = msg.args.into_iter(); |
486 | match msg.opcode { |
487 | #(#match_arms),* |
488 | _ => Err(DispatchError::BadMessage { sender_id: msg.sender_id, interface: Self::interface().name, opcode: msg.opcode }), |
489 | } |
490 | } |
491 | } |
492 | |
493 | pub(crate) fn gen_write_body(interface: &Interface, side: Side) -> TokenStream { |
494 | let msgs = match side { |
495 | Side::Client => &interface.requests, |
496 | Side::Server => &interface.events, |
497 | }; |
498 | let msg_type = Ident::new( |
499 | match side { |
500 | Side::Client => "Request" , |
501 | Side::Server => "Event" , |
502 | }, |
503 | Span::call_site(), |
504 | ); |
505 | let arms = msgs.iter().enumerate().map(|(opcode, msg)| { |
506 | let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site()); |
507 | let opcode = opcode as u16; |
508 | let arg_names = msg.args.iter().flat_map(|arg| { |
509 | if arg.typ == Type::NewId && arg.interface.is_some() && side == Side::Client { |
510 | None |
511 | } else { |
512 | Some(format_ident!(" {}{}" , if is_keyword(&arg.name) { "_" } else { "" }, arg.name)) |
513 | } |
514 | }); |
515 | let mut child_spec = None; |
516 | let args = msg.args.iter().flat_map(|arg| { |
517 | let arg_name = format_ident!(" {}{}" , if is_keyword(&arg.name) { "_" } else { "" }, arg.name); |
518 | |
519 | match arg.typ { |
520 | Type::Int => vec![if arg.enum_.is_some() { quote!{ Argument::Int(Into::<u32>::into(#arg_name) as i32) } } else { quote!{ Argument::Int(#arg_name) } }], |
521 | Type::Uint => vec![if arg.enum_.is_some() { quote!{ Argument::Uint(#arg_name.into()) } } else { quote!{ Argument::Uint(#arg_name) } }], |
522 | Type::Fd => vec![quote!{ Argument::Fd(#arg_name) }], |
523 | Type::Fixed => vec![quote! { Argument::Fixed((#arg_name * 256.) as i32) }], |
524 | Type::Object => if arg.allow_null { |
525 | if side == Side::Server { |
526 | vec![quote! { if let Some(obj) = #arg_name { Argument::Object(Resource::id(&obj)) } else { Argument::Object(ObjectId::null()) } }] |
527 | } else { |
528 | vec![quote! { if let Some(obj) = #arg_name { Argument::Object(Proxy::id(&obj)) } else { Argument::Object(ObjectId::null()) } }] |
529 | } |
530 | } else if side == Side::Server { |
531 | vec![quote!{ Argument::Object(Resource::id(&#arg_name)) }] |
532 | } else { |
533 | vec![quote!{ Argument::Object(Proxy::id(&#arg_name)) }] |
534 | }, |
535 | Type::Array => if arg.allow_null { |
536 | vec![quote! { if let Some(array) = #arg_name { Argument::Array(Box::new(array)) } else { Argument::Array(Box::new(Vec::new()))}}] |
537 | } else { |
538 | vec![quote! { Argument::Array(Box::new(#arg_name)) }] |
539 | }, |
540 | Type::String => if arg.allow_null { |
541 | vec![quote! { Argument::Str(#arg_name.map(|s| Box::new(std::ffi::CString::new(s).unwrap()))) }] |
542 | } else { |
543 | vec![quote! { Argument::Str(Some(Box::new(std::ffi::CString::new(#arg_name).unwrap()))) }] |
544 | }, |
545 | Type::NewId => if side == Side::Client { |
546 | if let Some(ref created_interface) = arg.interface { |
547 | let created_iface_mod = Ident::new(created_interface, Span::call_site()); |
548 | let created_iface_type = Ident::new(&snake_to_camel(created_interface), Span::call_site()); |
549 | assert!(child_spec.is_none()); |
550 | child_spec = Some(quote! { { |
551 | let my_info = conn.object_info(self.id())?; |
552 | Some((super::#created_iface_mod::#created_iface_type::interface(), my_info.version)) |
553 | } }); |
554 | vec![quote! { Argument::NewId(ObjectId::null()) }] |
555 | } else { |
556 | assert!(child_spec.is_none()); |
557 | child_spec = Some(quote! { |
558 | Some((#arg_name.0, #arg_name.1)) |
559 | }); |
560 | vec![ |
561 | quote! { |
562 | Argument::Str(Some(Box::new(std::ffi::CString::new(#arg_name.0.name).unwrap()))) |
563 | }, |
564 | quote! { |
565 | Argument::Uint(#arg_name.1) |
566 | }, |
567 | quote! { |
568 | Argument::NewId(ObjectId::null()) |
569 | }, |
570 | ] |
571 | } |
572 | } else { |
573 | // server-side NewId is the same as Object |
574 | if arg.allow_null { |
575 | vec![quote! { if let Some(obj) = #arg_name { Argument::NewId(Resource::id(&obj)) } else { Argument::NewId(ObjectId::null()) } }] |
576 | } else { |
577 | vec![quote!{ Argument::NewId(Resource::id(&#arg_name)) }] |
578 | } |
579 | }, |
580 | Type::Destructor => panic!("Argument {}. {}. {} has type destructor ?!" , interface.name, msg.name, arg.name), |
581 | } |
582 | }); |
583 | let args = if msg.args.is_empty() { |
584 | quote! { |
585 | smallvec::SmallVec::new() |
586 | } |
587 | } else if msg.args.len() <= 4 { |
588 | // Note: Keep in sync with `wayland_backend::protocol::INLINE_ARGS`. |
589 | // Fits in SmallVec inline capacity |
590 | quote! { { |
591 | let mut vec = smallvec::SmallVec::new(); |
592 | #( |
593 | vec.push(#args); |
594 | )* |
595 | vec |
596 | } } |
597 | } else { |
598 | quote! { |
599 | smallvec::SmallVec::from_vec(vec![#(#args),*]) |
600 | } |
601 | }; |
602 | if side == Side::Client { |
603 | let child_spec = child_spec.unwrap_or_else(|| quote! { None }); |
604 | quote! { |
605 | #msg_type::#msg_name { #(#arg_names),* } => { |
606 | let child_spec = #child_spec; |
607 | let args = #args; |
608 | Ok((Message { |
609 | sender_id: self.id.clone(), |
610 | opcode: #opcode, |
611 | args |
612 | }, child_spec)) |
613 | } |
614 | } |
615 | } else { |
616 | quote! { |
617 | #msg_type::#msg_name { #(#arg_names),* } => Ok(Message { |
618 | sender_id: self.id.clone(), |
619 | opcode: #opcode, |
620 | args: #args, |
621 | }) |
622 | } |
623 | } |
624 | }); |
625 | quote! { |
626 | match msg { |
627 | #(#arms,)* |
628 | #msg_type::__phantom_lifetime { never, .. } => match never {} |
629 | } |
630 | } |
631 | } |
632 | |