prpc_build/
client.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use super::{Method, Service};
use crate::{generate_doc_comments, naive_snake_case, Builder};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

/// Generate service for client.
///
/// This takes some `Service` and will generate a `TokenStream` that contains
/// a public module with the generated client.
pub fn generate<T: Service>(service: &T, config: &Builder) -> TokenStream {
    let attributes = &config.client_attributes;
    let service_ident = quote::format_ident!("{}Client", service.name());
    let client_mod = quote::format_ident!("{}_client", naive_snake_case(service.name()));
    let methods = generate_methods(service, config);

    let service_doc = generate_doc_comments(service.comment());
    let mod_attributes = attributes.for_mod(service.package());
    let struct_attributes = attributes.for_struct(service.identifier());

    quote! {
        /// Generated client implementations.
        #(#mod_attributes)*
        pub mod #client_mod {
            #service_doc
            #(#struct_attributes)*
            #[derive(Debug)]
            pub struct #service_ident<Client> {
                pub client: Client,
            }

            impl<Client> #service_ident<Client>
            where
                Client: ::prpc::client::RequestClient
            {
                pub fn new(client: Client) -> Self {
                    Self { client }
                }

                #methods
            }
        }
    }
}

fn generate_methods<T: Service>(service: &T, config: &Builder) -> TokenStream {
    let mut stream = TokenStream::new();
    for method in service.methods() {
        let path = crate::join_path(
            config,
            service.package(),
            service.identifier(),
            method.identifier(),
        );

        stream.extend(generate_doc_comments(method.comment()));

        let method = match (method.client_streaming(), method.server_streaming()) {
            (false, false) => generate_unary(method, config, path),
            _ => {
                panic!("Only unary method supported");
            }
        };

        stream.extend(method);
    }

    stream
}

fn generate_unary<T: Method>(method: &T, config: &Builder, path: String) -> TokenStream {
    let ident = format_ident!("{}", method.name());
    let (request, response) =
        method.request_response_name(&config.proto_path, config.compile_well_known_types);

    template_quote::quote! {
        pub async fn #ident(
            &self
            #(if request.is_some())
            {
                , request: #request,
            }
        ) -> Result<#response, ::prpc::client::Error> {
            #(if request.is_none())
            {
                let request = ();
            }
            let response = self.client.request(#path, ::prpc::codec::encode_message_to_vec(&request)).await?;
            Ok(::prpc::Message::decode(&response[..])?)
        }
    }
}