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