waynest_gen/
common.rs

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}