1use heck::ToSnekCase;
2use proc_macro2::TokenStream;
3use quote::quote;
4
5use crate::{
6 parser::{ArgType, Interface, Message, MessageType},
7 utils::make_ident,
8};
9
10pub fn write_dispatchers<I: Iterator<Item = Message>>(
11 interface: &Interface,
12 messages: I,
13) -> Vec<TokenStream> {
14 let mut dispatchers = Vec::new();
15
16 for (opcode, request) in messages.enumerate() {
17 let opcode = opcode as u16;
18 let name = make_ident(request.name.to_snek_case());
19
20 let mut tracing_fmt = Vec::new();
21 let mut tracing_args = Vec::new();
22
23 let mut args = vec![quote! { client }, quote! { sender_id }];
24 let mut setters = Vec::new();
25
26 for arg in &request.args {
27 let mut optional = quote! {};
28 let mut map_display = quote! {};
29
30 if !arg.allow_null && arg.is_return_option() {
31 optional = quote! {.ok_or(crate::wire::DecodeError::MalformedPayload)?};
32 } else if arg.allow_null {
33 map_display = quote! {.as_ref().map_or("null".to_string(), |v| v.to_string())}
34 }
35
36 let mut tryinto = quote! {};
37
38 if arg.r#enum.is_some() {
39 tryinto = quote! {.try_into()?}
40 }
41
42 let caller = make_ident(arg.to_caller());
43
44 let name = make_ident(arg.name.to_snek_case());
45
46 setters.push(quote! {
47 let #name = message.#caller()? #optional;
48 });
49
50 args.push(quote! {
51 #name #tryinto
52 });
53
54 match arg.ty {
55 ArgType::Array => {
56 tracing_fmt.push("array[{}]");
57 tracing_args.push(quote! { #name .len() });
58 }
59 ArgType::String => {
60 tracing_fmt.push("\"{}\"");
61 tracing_args.push(quote! { #name #map_display });
62 }
63 ArgType::Fd => {
64 tracing_fmt.push("{}");
65 tracing_args.push(quote! { #name .as_raw_fd() #map_display });
66 }
67 _ => {
68 tracing_fmt.push("{}");
69 tracing_args.push(quote! { #name #map_display });
70 }
71 }
72 }
73
74 let tracing_fmt = tracing_fmt.join(", ");
75
76 let tracing_inner = format!(
77 "{interface}#{{}}.{request}({tracing_fmt})",
78 interface = interface.name,
79 request = request.name.to_snek_case()
80 );
81
82 let result = if request.ty == Some(MessageType::Destructor) {
83 quote! {
84 let result = self.#name(#(#args),*).await;
85 client.remove(sender_id);
86 result
87 }
88 } else {
89 quote! {
90 self.#name(#(#args),*).await
91 }
92 };
93
94 let inner = quote! {
95 #opcode => {
96 #(#setters)*
97
98 tracing::debug!(#tracing_inner, sender_id, #(#tracing_args),*);
99 #result
100 }
101 };
102
103 dispatchers.push(inner);
104 }
105
106 dispatchers
107}