1 #[allow(unused_imports)]
2 #[allow(deprecated)]
3 use std::ascii::AsciiExt;
4 use std::iter;
5 use std::str;
6 
7 use proc_macro2::{Ident, Literal, Span, TokenStream};
8 use quote::{quote, ToTokens};
9 
10 use crate::protocol::*;
11 use crate::util::*;
12 use crate::Side;
13 
to_doc_attr(text: &str) -> TokenStream14 pub(crate) fn to_doc_attr(text: &str) -> TokenStream {
15     let text = text.lines().map(str::trim).collect::<Vec<_>>().join("\n");
16     let text = text.trim();
17 
18     quote!(#[doc = #text])
19 }
20 
description_to_doc_attr(&(ref short, ref long): &(String, String)) -> TokenStream21 pub(crate) fn description_to_doc_attr(&(ref short, ref long): &(String, String)) -> TokenStream {
22     to_doc_attr(&format!("{}\n\n{}", short, long))
23 }
24 
25 impl ToTokens for Enum {
to_tokens(&self, tokens: &mut TokenStream)26     fn to_tokens(&self, tokens: &mut TokenStream) {
27         let enum_decl;
28         let enum_impl;
29 
30         let doc_attr = self.description.as_ref().map(description_to_doc_attr);
31         let ident = Ident::new(&snake_to_camel(&self.name), Span::call_site());
32 
33         if self.bitfield {
34             let entries = self.entries.iter().map(|entry| {
35                 let doc_attr = entry
36                     .description
37                     .as_ref()
38                     .map(description_to_doc_attr)
39                     .or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s)));
40 
41                 let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" };
42                 let ident = Ident::new(
43                     &format!("{}{}", prefix, snake_to_camel(&entry.name)),
44                     Span::call_site(),
45                 );
46 
47                 let value = Literal::u32_unsuffixed(entry.value);
48 
49                 quote! {
50                     #doc_attr
51                     const #ident = #value;
52                 }
53             });
54 
55             enum_decl = quote! {
56                 bitflags! {
57                     #doc_attr
58                     pub struct #ident: u32 {
59                         #(#entries)*
60                     }
61                 }
62             };
63             enum_impl = quote! {
64                 impl #ident {
65                     pub fn from_raw(n: u32) -> Option<#ident> {
66                         Some(#ident::from_bits_truncate(n))
67                     }
68 
69                     pub fn to_raw(&self) -> u32 {
70                         self.bits()
71                     }
72                 }
73             };
74         } else {
75             let variants = self.entries.iter().map(|entry| {
76                 let doc_attr = entry
77                     .description
78                     .as_ref()
79                     .map(description_to_doc_attr)
80                     .or_else(|| entry.summary.as_ref().map(|s| to_doc_attr(s)));
81 
82                 let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" };
83                 let variant = Ident::new(
84                     &format!("{}{}", prefix, snake_to_camel(&entry.name)),
85                     Span::call_site(),
86                 );
87 
88                 let value = Literal::u32_unsuffixed(entry.value);
89 
90                 quote! {
91                     #doc_attr
92                     #variant = #value
93                 }
94             });
95 
96             enum_decl = quote! {
97                 #doc_attr
98                 #[repr(u32)]
99                 #[derive(Copy, Clone, Debug, PartialEq)]
100                 #[non_exhaustive]
101                 pub enum #ident {
102                     #(#variants,)*
103                 }
104             };
105 
106             let match_arms = self.entries.iter().map(|entry| {
107                 let value = Literal::u32_unsuffixed(entry.value);
108 
109                 let prefix = if entry.name.chars().next().unwrap().is_numeric() { "_" } else { "" };
110                 let variant = Ident::new(
111                     &format!("{}{}", prefix, snake_to_camel(&entry.name)),
112                     Span::call_site(),
113                 );
114 
115                 quote! {
116                     #value => Some(#ident::#variant)
117                 }
118             });
119 
120             enum_impl = quote! {
121                 impl #ident {
122                     pub fn from_raw(n: u32) -> Option<#ident> {
123                         match n {
124                             #(#match_arms,)*
125                             _ => Option::None
126                         }
127                     }
128 
129                     pub fn to_raw(&self) -> u32 {
130                         *self as u32
131                     }
132                 }
133             };
134         }
135 
136         enum_decl.to_tokens(tokens);
137         enum_impl.to_tokens(tokens);
138     }
139 }
140 
gen_since_constants(requests: &[Message], events: &[Message]) -> TokenStream141 pub(crate) fn gen_since_constants(requests: &[Message], events: &[Message]) -> TokenStream {
142     let req_constants = requests.iter().map(|msg| {
143         let cstname =
144             Ident::new(&format!("REQ_{}_SINCE", msg.name.to_ascii_uppercase()), Span::call_site());
145         let since = msg.since;
146         quote! {
147             /// The minimal object version supporting this request
148             pub const #cstname: u32 = #since;
149         }
150     });
151     let evt_constants = events.iter().map(|msg| {
152         let cstname =
153             Ident::new(&format!("EVT_{}_SINCE", msg.name.to_ascii_uppercase()), Span::call_site());
154         let since = msg.since;
155         quote! {
156             /// The minimal object version supporting this event
157             pub const #cstname: u32 = #since;
158         }
159     });
160 
161     quote! {
162         #(#req_constants)*
163         #(#evt_constants)*
164     }
165 }
166 
gen_messagegroup( name: &Ident, side: Side, receiver: bool, messages: &[Message], addon: Option<TokenStream>, ) -> TokenStream167 pub(crate) fn gen_messagegroup(
168     name: &Ident,
169     side: Side,
170     receiver: bool,
171     messages: &[Message],
172     addon: Option<TokenStream>,
173 ) -> TokenStream {
174     let variants = messages.iter().map(|msg| {
175         let mut docs = String::new();
176         if let Some((ref short, ref long)) = msg.description {
177             docs += &format!("{}\n\n{}\n", short, long.trim());
178         }
179         if let Some(Type::Destructor) = msg.typ {
180             docs += &format!(
181                 "\nThis is a destructor, once {} this object cannot be used any longer.",
182                 if receiver { "received" } else { "sent" },
183             );
184         }
185         if msg.since > 1 {
186             docs += &format!("\nOnly available since version {} of the interface", msg.since);
187         }
188 
189         let doc_attr = to_doc_attr(&docs);
190         let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
191         let msg_variant_decl = if msg.args.is_empty() {
192             msg_name.into_token_stream()
193         } else {
194             let fields = msg.args.iter().flat_map(|arg| {
195                 let field_name = Ident::new(
196                     &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
197                     Span::call_site(),
198                 );
199                 let field_type_inner = if let Some(ref enu) = arg.enum_ {
200                     dotted_to_relname(enu)
201                 } else {
202                     match arg.typ {
203                         Type::Uint => quote!(u32),
204                         Type::Int => quote!(i32),
205                         Type::Fixed => quote!(f64),
206                         Type::String => quote!(String),
207                         Type::Array => quote!(Vec<u8>),
208                         Type::Fd => quote!(::std::os::unix::io::RawFd),
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 {
216                                 quote!(AnonymousObject)
217                             }
218                         }
219                         Type::NewId if !receiver && side == Side::Client => {
220                             // Client-side sending does not have a pre-existing object
221                             // so skip serializing it
222                             if arg.interface.is_some() {
223                                 return None;
224                             } else {
225                                 quote!((String, u32))
226                             }
227                         }
228                         Type::NewId => {
229                             let object_name = if side == Side::Server && !receiver {
230                                 Ident::new("Resource", Span::call_site())
231                             } else {
232                                 Ident::new("Main", Span::call_site())
233                             };
234                             if let Some(ref iface) = arg.interface {
235                                 let iface_mod = Ident::new(&iface, Span::call_site());
236                                 let iface_type =
237                                     Ident::new(&snake_to_camel(iface), Span::call_site());
238                                 quote!(#object_name<super::#iface_mod::#iface_type>)
239                             } else {
240                                 // bind-like function
241                                 quote!((String, u32, AnonymousObject))
242                             }
243                         }
244                         Type::Destructor => panic!("An argument cannot have type \"destructor\"."),
245                     }
246                 };
247 
248                 let field_type = if arg.allow_null {
249                     quote!(Option<#field_type_inner>)
250                 } else {
251                     field_type_inner.into_token_stream()
252                 };
253                 Some(quote! {
254                     #field_name: #field_type
255                 })
256             });
257 
258             quote! {
259                 #msg_name {
260                     #(#fields,)*
261                 }
262             }
263         };
264 
265         quote! {
266             #doc_attr
267             #msg_variant_decl
268         }
269     });
270 
271     let message_array_values = messages.iter().map(|msg| {
272         let name_value = &msg.name;
273         let since_value = Literal::u32_unsuffixed(msg.since);
274         let signature_values = msg.args.iter().map(|arg| {
275             let common_type = arg.typ.common_type();
276             quote!(super::ArgumentType::#common_type)
277         });
278         let is_destructor = msg.typ == Some(Type::Destructor);
279 
280         quote! {
281             super::MessageDesc {
282                 name: #name_value,
283                 since: #since_value,
284                 signature: &[
285                     #(#signature_values,)*
286                 ],
287                 destructor: #is_destructor,
288             }
289         }
290     });
291 
292     let map_type = if side == Side::Client { quote!(ProxyMap) } else { quote!(ResourceMap) };
293 
294     // Can't be a closure because closures are never Copy / Clone in rustc < 1.26.0, and we supports 1.21.0
295     fn map_fn((ref msg, ref name): (&Message, &Ident)) -> TokenStream {
296         let msg_type = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
297         let msg_type_qualified = quote!(#name::#msg_type);
298 
299         if msg.args.is_empty() {
300             msg_type_qualified
301         } else {
302             quote!(#msg_type_qualified { .. })
303         }
304     };
305 
306     let message_match_patterns = messages.iter().zip(iter::repeat(name)).map(map_fn);
307 
308     let mut is_destructor_match_arms = messages
309         .iter()
310         .zip(message_match_patterns.clone())
311         .filter(|&(msg, _)| msg.typ == Some(Type::Destructor))
312         .map(|(_, pattern)| quote!(#pattern => true))
313         .collect::<Vec<_>>();
314 
315     if messages.len() > is_destructor_match_arms.len() {
316         is_destructor_match_arms.push(quote!(_ => false));
317     }
318 
319     let opcode_match_arms = message_match_patterns.clone().enumerate().map(|(opcode, pattern)| {
320         let value = Literal::u16_unsuffixed(opcode as u16);
321         quote!(#pattern => #value)
322     });
323 
324     let since_match_arms = messages.iter().zip(message_match_patterns).map(|(msg, pattern)| {
325         let since = Literal::u32_unsuffixed(msg.since as u32);
326         quote!(#pattern => #since)
327     });
328 
329     let child_match_arms = messages
330         .iter()
331         .enumerate()
332         .filter_map(|(opcode, msg)| {
333             let mut it = msg.args.iter().filter_map(|a| {
334                 if a.typ == Type::NewId {
335                     a.interface.as_ref()
336                 } else {
337                     None
338                 }
339             });
340 
341             it.next().map(|new_iface| {
342                 assert!(
343                     it.next().is_none(),
344                     "Got a message with more than one new_id in {}.{}",
345                     name,
346                     msg.name
347                 );
348 
349                 let pattern = Literal::u16_unsuffixed(opcode as u16);
350                 let new_iface_mod = Ident::new(new_iface, Span::call_site());
351                 let new_iface_type = Ident::new(&snake_to_camel(new_iface), Span::call_site());
352 
353                 quote! {
354                     #pattern => Some(Object::from_interface::<super::#new_iface_mod::#new_iface_type>(
355                         version,
356                         meta.child(),
357                     ))
358                 }
359             })
360         })
361         .chain(iter::once(quote!(_ => None)));
362 
363     let from_raw_body = if receiver {
364         let match_arms = messages
365             .iter()
366             .enumerate()
367             .map(|(opcode, msg)| {
368                 let pattern = Literal::u16_unsuffixed(opcode as u16);
369                 let msg_type = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
370                 let msg_type_qualified = quote!(#name::#msg_type);
371 
372                 let block = if msg.args.is_empty() {
373                     quote!(Ok(#msg_type_qualified))
374                 } else {
375                     let fields = msg.args.iter().map(|arg| {
376                         let field_name = Ident::new(
377                             &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
378                             Span::call_site(),
379                         );
380                         let some_code_path = match arg.typ {
381                             Type::Int => {
382                                 if let Some(ref enu) = arg.enum_ {
383                                     let enum_ident = dotted_to_relname(enu);
384                                     quote!(#enum_ident::from_raw(val as u32).ok_or(())?)
385                                 } else {
386                                     quote!(val)
387                                 }
388                             }
389                             Type::Uint => {
390                                 if let Some(ref enu) = arg.enum_ {
391                                     let enum_ident = dotted_to_relname(enu);
392                                     quote!(#enum_ident::from_raw(val).ok_or(())?)
393                                 } else {
394                                     quote!(val)
395                                 }
396                             }
397                             Type::Fixed => quote!((val as f64) / 256.),
398                             Type::Array => {
399                                 if arg.allow_null {
400                                     quote!(if val.len() == 0 { None } else { Some(*val) })
401                                 } else {
402                                     quote!(*val)
403                                 }
404                             }
405                             Type::String => {
406                                 let string_conversion = quote! {
407                                     let s = String::from_utf8(val.into_bytes())
408                                         .unwrap_or_else(|e| String::from_utf8_lossy(&e.into_bytes()).into());
409                                 };
410 
411                                 if arg.allow_null {
412                                     quote! {
413                                         #string_conversion
414                                         if s.len() == 0 { None } else { Some(s) }
415                                     }
416                                 } else {
417                                     quote! {
418                                         #string_conversion
419                                         s
420                                     }
421                                 }
422                             }
423                             Type::Fd => quote!(val),
424                             Type::Object => {
425                                 let map_lookup = if side == Side::Client {
426                                     quote!(map.get_or_dead(val).into())
427                                 } else {
428                                     quote!(map.get(val).ok_or(())?.into())
429                                 };
430                                 if arg.allow_null {
431                                     quote!(if val == 0 { None } else { Some(#map_lookup) })
432                                 } else {
433                                     map_lookup
434                                 }
435                             }
436                             Type::NewId => {
437                                 let map_lookup = quote!(map.get_new(val).ok_or(())?);
438                                 if arg.allow_null {
439                                     quote!(if val == 0 { None } else { Some(#map_lookup) })
440                                 } else {
441                                     map_lookup
442                                 }
443                             }
444                             Type::Destructor => panic!("An argument cannot have type destructor!"),
445                         };
446 
447                         let common_type = arg.typ.common_type();
448 
449                         quote! {
450                             #field_name: {
451                                 if let Some(Argument::#common_type(val)) = args.next() {
452                                     #some_code_path
453                                 } else {
454                                     return Err(());
455                                 }
456                             }
457                         }
458                     });
459 
460                     quote! {
461                         {
462                             let mut args = msg.args.into_iter();
463 
464                             Ok(#msg_type_qualified {
465                                 #(#fields,)*
466                             })
467                         }
468                     }
469                 };
470 
471                 quote!(#pattern => #block)
472             })
473             .chain(iter::once(quote!(_ => Err(()))));
474 
475         quote! {
476             match msg.opcode {
477                 #(#match_arms,)*
478             }
479         }
480     } else {
481         let panic_message = format!("{}::from_raw can not be used {:?}-side.", name, side);
482         quote!(panic!(#panic_message))
483     };
484 
485     let into_raw_body = if receiver {
486         let panic_message = format!("{}::into_raw can not be used {:?}-side.", name, side);
487         quote!(panic!(#panic_message))
488     } else {
489         let match_arms = messages.iter().enumerate().map(|(opcode, msg)| {
490             let msg_type = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
491             let msg_type_qualified = quote!(#name::#msg_type);
492 
493             let pattern = if msg.args.is_empty() {
494                 msg_type_qualified
495             } else {
496                 let fields = msg.args.iter().flat_map(|arg| {
497                     // Client-side newid request do not contain a placeholder
498                     if side == Side::Client && arg.typ == Type::NewId && arg.interface.is_some() {
499                         None
500                     } else {
501                         Some(Ident::new(
502                             &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
503                             Span::call_site(),
504                         ))
505                     }
506                 });
507                 quote!(#msg_type_qualified { #(#fields),* })
508             };
509 
510             let opcode_value = Literal::u16_unsuffixed(opcode as u16);
511             let args_values = msg.args.iter().map(|arg| {
512                 let arg_ident = Ident::new(
513                     &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
514                     Span::call_site(),
515                 );
516                 match arg.typ {
517                     Type::Int => {
518                         if arg.enum_.is_some() {
519                             quote!(Argument::Int(#arg_ident.to_raw() as i32))
520                         } else {
521                             quote!(Argument::Int(#arg_ident))
522                         }
523                     }
524                     Type::Uint => {
525                         if arg.enum_.is_some() {
526                             quote!(Argument::Uint(#arg_ident.to_raw()))
527                         } else {
528                             quote!(Argument::Uint(#arg_ident))
529                         }
530                     }
531                     Type::Fixed => quote!(Argument::Fixed((#arg_ident * 256.) as i32)),
532                     Type::String => {
533                         if arg.allow_null {
534                             quote! {
535                                 Argument::Str(Box::new(unsafe {
536                                     ::std::ffi::CString::from_vec_unchecked(
537                                         #arg_ident.map(Into::into).unwrap_or_else(Vec::new),
538                                     )
539                                 }))
540                             }
541                         } else {
542                             quote! {
543                                 Argument::Str(Box::new(unsafe {
544                                     ::std::ffi::CString::from_vec_unchecked(#arg_ident.into())
545                                 }))
546                             }
547                         }
548                     }
549                     Type::Array => {
550                         if arg.allow_null {
551                             quote!(Argument::Array(Box::new(#arg_ident.unwrap_or_else(Vec::new))))
552                         } else {
553                             quote!(Argument::Array(Box::new(#arg_ident)))
554                         }
555                     }
556                     Type::Fd => quote!(Argument::Fd(#arg_ident)),
557                     Type::NewId => {
558                         if arg.interface.is_some() {
559                             let id = if side == Side::Client {
560                                 quote!(0)
561                             } else {
562                                 quote!(#arg_ident.id())
563                             };
564                             quote!(Argument::NewId(#id))
565                         } else {
566                             let id = if side == Side::Client {
567                                 quote!(0)
568                             } else {
569                                 quote!(#arg_ident.2.id())
570                             };
571                             quote! {
572                                 Argument::Str(Box::new(unsafe {
573                                     ::std::ffi::CString::from_vec_unchecked(#arg_ident.0.into())
574                                 })),
575                                 Argument::Uint(#arg_ident.1),
576                                 Argument::NewId(#id)
577                             }
578                         }
579                     }
580                     Type::Object => {
581                         if arg.allow_null {
582                             quote!(Argument::Object(#arg_ident.map(|o| o.as_ref().id()).unwrap_or(0)))
583                         } else {
584                             quote!(Argument::Object(#arg_ident.as_ref().id()))
585                         }
586                     }
587                     Type::Destructor => panic!("An argument cannot have type Destructor"),
588                 }
589             });
590 
591             quote!(#pattern => Message {
592                 sender_id: sender_id,
593                 opcode: #opcode_value,
594                 args: smallvec![
595                     #(#args_values,)*
596                 ],
597             })
598         });
599 
600         quote! {
601             match self {
602                 #(#match_arms,)*
603             }
604         }
605     };
606 
607     quote! {
608         #[derive(Debug)]
609         #[non_exhaustive]
610         pub enum #name {
611             #(#variants,)*
612         }
613 
614         impl super::MessageGroup for #name {
615             const MESSAGES: &'static [super::MessageDesc] = &[
616                 #(#message_array_values,)*
617             ];
618 
619             type Map = super::#map_type;
620 
621             fn is_destructor(&self) -> bool {
622                 match *self {
623                     #(#is_destructor_match_arms,)*
624                 }
625             }
626 
627             fn opcode(&self) -> u16 {
628                 match *self {
629                     #(#opcode_match_arms,)*
630                 }
631             }
632 
633             fn since(&self) -> u32 {
634                 match *self {
635                     #(#since_match_arms,)*
636                 }
637             }
638 
639             fn child<Meta: ObjectMetadata>(opcode: u16, version: u32, meta: &Meta) -> Option<Object<Meta>> {
640                 match opcode {
641                     #(#child_match_arms,)*
642                 }
643             }
644 
645             fn from_raw(msg: Message, map: &mut Self::Map) -> Result<Self, ()> {
646                 #from_raw_body
647             }
648 
649             fn into_raw(self, sender_id: u32) -> Message {
650                 #into_raw_body
651             }
652 
653             #addon
654         }
655     }
656 }
657 
gen_interface( name: &Ident, low_name: &str, version: u32, addon: Option<TokenStream>, side: Side, ) -> TokenStream658 pub(crate) fn gen_interface(
659     name: &Ident,
660     low_name: &str,
661     version: u32,
662     addon: Option<TokenStream>,
663     side: Side,
664 ) -> TokenStream {
665     let object_type = side.object_name();
666     let version_lit = Literal::u32_unsuffixed(version);
667 
668     quote! {
669         #[derive(Clone, Eq, PartialEq)]
670         pub struct #name(#object_type<#name>);
671 
672         impl AsRef<#object_type<#name>> for #name {
673             #[inline]
674             fn as_ref(&self) -> &#object_type<Self> {
675                 &self.0
676             }
677         }
678 
679         impl From<#object_type<#name>> for #name {
680             #[inline]
681             fn from(value: #object_type<Self>) -> Self {
682                 #name(value)
683             }
684         }
685 
686         impl From<#name> for #object_type<#name> {
687             #[inline]
688             fn from(value: #name) -> Self {
689                 value.0
690             }
691         }
692 
693         impl std::fmt::Debug for #name {
694             fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
695                 f.write_fmt(format_args!("{:?}", self.0))
696             }
697         }
698 
699         impl Interface for #name {
700             type Request = Request;
701             type Event = Event;
702             const NAME: &'static str = #low_name;
703             const VERSION: u32 = #version_lit;
704 
705             #addon
706         }
707     }
708 }
709 
method_prototype<'a>( iname: &Ident, msg: &'a Message, side: Side, ) -> (TokenStream, Option<&'a Arg>)710 pub fn method_prototype<'a>(
711     iname: &Ident,
712     msg: &'a Message,
713     side: Side,
714 ) -> (TokenStream, Option<&'a Arg>) {
715     let mut it = msg.args.iter().filter(|arg| arg.typ == Type::NewId);
716     let mut newid = it.next();
717     assert!(
718         newid.is_none() || it.next().is_none(),
719         "Request {}.{} returns more than one new_id",
720         iname,
721         msg.name
722     );
723 
724     // Serverside we don't deal with NewId arguments and treat them as objects.
725     if side == Side::Server {
726         newid = None;
727     }
728 
729     let fn_name = Ident::new(
730         &format!("{}{}", if is_keyword(&msg.name) { "_" } else { "" }, msg.name),
731         Span::call_site(),
732     );
733 
734     let mut args = Vec::new();
735 
736     let generics = if let Some(arg) = newid {
737         if arg.interface.is_none() {
738             args.push(quote!(version: u32));
739             Some(quote!(T: Interface + From<Proxy<T>> + AsRef<Proxy<T>>))
740         } else {
741             None
742         }
743     } else {
744         None
745     }
746     .into_iter();
747 
748     args.extend(msg.args.iter().filter_map(|arg| {
749         let arg_type_inner = if let Some(ref name) = arg.enum_ {
750             dotted_to_relname(name)
751         } else {
752             let mut typ = arg.typ;
753             if typ == Type::NewId && side == Side::Server {
754                 typ = Type::Object;
755             }
756             match typ {
757                 Type::Object => arg
758                     .interface
759                     .as_ref()
760                     .map(|iface| {
761                         let iface_mod = Ident::new(iface, Span::call_site());
762                         let iface_type = Ident::new(&snake_to_camel(iface), Span::call_site());
763                         quote!(&super::#iface_mod::#iface_type)
764                     })
765                     .unwrap_or(quote!(&super::AnonymousObject)),
766                 Type::NewId => {
767                     // client-side, the return-type handles that
768                     return None;
769                 }
770                 _ => arg.typ.rust_type(),
771             }
772         };
773 
774         let arg_name = Ident::new(
775             &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
776             Span::call_site(),
777         );
778 
779         let arg_type =
780             if arg.allow_null { quote!(Option<#arg_type_inner>) } else { arg_type_inner };
781 
782         Some(quote!(#arg_name: #arg_type))
783     }));
784 
785     let return_type = if let Some(arg) = newid {
786         match arg.interface {
787             Some(ref iface) => {
788                 let iface_mod = Ident::new(&iface, Span::call_site());
789                 let iface_type = Ident::new(&snake_to_camel(&iface), Span::call_site());
790 
791                 quote!(Main<super::#iface_mod::#iface_type>)
792             }
793             None => quote!(Main<T>),
794         }
795     } else {
796         quote!(())
797     };
798 
799     let prototype = quote! {
800         pub fn #fn_name#(<#generics>)*(&self, #(#args),*) -> #return_type
801     };
802 
803     (prototype, newid)
804 }
805 
gen_object_methods(name: &Ident, messages: &[Message], side: Side) -> TokenStream806 pub(crate) fn gen_object_methods(name: &Ident, messages: &[Message], side: Side) -> TokenStream {
807     let outgoing_message_type = Ident::new(
808         match side {
809             Side::Client => "Request",
810             Side::Server => "Event",
811         },
812         Span::call_site(),
813     );
814 
815     let method_impls = messages.iter().map(|msg| {
816         let mut docs = String::new();
817         if let Some((ref short, ref long)) = msg.description {
818             docs += &format!("{}\n\n{}\n", short, long);
819         }
820         if let Some(Type::Destructor) = msg.typ {
821             docs += "\nThis is a destructor, you cannot send requests to this object any longer once this method is called.";
822         }
823         if msg.since > 1 {
824             docs += &format!("\nOnly available since version {} of the interface.", msg.since);
825         }
826 
827         let doc_attr = to_doc_attr(&docs);
828 
829         let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
830         let (proto, return_type) = method_prototype(name, &msg, side);
831 
832         let msg_init = if msg.args.is_empty() {
833             TokenStream::new()
834         } else {
835             let args = msg.args.iter().flat_map(|arg| {
836                 let arg_name = Ident::new(
837                     &format!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name),
838                     Span::call_site(),
839                 );
840                 let arg_value = match (arg.typ, side) {
841                     (Type::NewId, Side::Client) => {
842                         if arg.interface.is_some() {
843                             return None;
844                         } else {
845                             quote!((T::NAME.into(), version))
846                         }
847                     },
848                     (Type::NewId, Side::Server) => {
849                         if arg.allow_null {
850                             quote!(#arg_name.map(|o| o.as_ref().clone()))
851                         } else {
852                             quote!(#arg_name.as_ref().clone())
853                         }
854                     }
855                     (Type::Object, _) => {
856                         if arg.allow_null {
857                             quote!(#arg_name.map(|o| o.clone()))
858                         } else {
859                             quote!(#arg_name.clone())
860                         }
861                     }
862                     _ => quote!(#arg_name),
863                 };
864 
865                 Some(quote!(#arg_name: #arg_value))
866             });
867 
868             quote!({ #(#args),* })
869         };
870 
871         let send_stmt = match return_type {
872             Some(ret_type) if ret_type.interface.is_none() => {
873                 quote!(self.0.send(msg, Some(version)).unwrap())
874             }
875             Some(_) => quote!(self.0.send(msg, None).unwrap()),
876             None => if side == Side::Client {
877                 quote! { self.0.send::<AnonymousObject>(msg, None); }
878             } else {
879                 quote! { self.0.send(msg); }
880             }
881         };
882 
883         quote! {
884             #doc_attr
885             #proto {
886                 let msg = #outgoing_message_type::#msg_name #msg_init;
887                 #send_stmt
888             }
889         }
890     });
891 
892     quote! {
893         impl #name {
894             #(#method_impls)*
895         }
896     }
897 }
898