prpc_build/
client.rs

1use super::{Method, Service};
2use crate::{generate_doc_comments, naive_snake_case, Builder};
3use proc_macro2::TokenStream;
4use quote::{format_ident, quote};
5
6/// Generate service for client.
7///
8/// This takes some `Service` and will generate a `TokenStream` that contains
9/// a public module with the generated client.
10pub fn generate<T: Service>(service: &T, config: &Builder) -> TokenStream {
11    let attributes = &config.client_attributes;
12    let service_ident = quote::format_ident!("{}Client", service.name());
13    let client_mod = quote::format_ident!("{}_client", naive_snake_case(service.name()));
14    let methods = generate_methods(service, config);
15
16    let service_doc = generate_doc_comments(service.comment());
17    let mod_attributes = attributes.for_mod(service.package());
18    let struct_attributes = attributes.for_struct(service.identifier());
19
20    quote! {
21        /// Generated client implementations.
22        #(#mod_attributes)*
23        pub mod #client_mod {
24            #service_doc
25            #(#struct_attributes)*
26            #[derive(Debug)]
27            pub struct #service_ident<Client> {
28                pub client: Client,
29            }
30
31            impl<Client> #service_ident<Client>
32            where
33                Client: ::prpc::client::RequestClient
34            {
35                pub fn new(client: Client) -> Self {
36                    Self { client }
37                }
38
39                #methods
40            }
41        }
42    }
43}
44
45fn generate_methods<T: Service>(service: &T, config: &Builder) -> TokenStream {
46    let mut stream = TokenStream::new();
47    for method in service.methods() {
48        let path = crate::join_path(
49            config,
50            service.package(),
51            service.identifier(),
52            method.identifier(),
53        );
54
55        stream.extend(generate_doc_comments(method.comment()));
56
57        let method = match (method.client_streaming(), method.server_streaming()) {
58            (false, false) => generate_unary(method, config, path),
59            _ => {
60                panic!("Only unary method supported");
61            }
62        };
63
64        stream.extend(method);
65    }
66
67    stream
68}
69
70fn generate_unary<T: Method>(method: &T, config: &Builder, path: String) -> TokenStream {
71    let ident = format_ident!("{}", method.name());
72    let (request, response) =
73        method.request_response_name(&config.proto_path, config.compile_well_known_types);
74
75    template_quote::quote! {
76        pub async fn #ident(
77            &self
78            #(if request.is_some())
79            {
80                , request: #request,
81            }
82        ) -> Result<#response, ::prpc::client::Error> {
83            #(if request.is_none())
84            {
85                let request = ();
86            }
87            self.client.request(#path, request).await
88        }
89    }
90}