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