actix_prost_build/
method.rs

1use crate::{config::HttpRule, Request, Response};
2use proc_macro2::{Ident, TokenStream};
3
4pub struct Method {
5    name: Ident,
6    // method: prost_build::Method,
7    config: HttpRule,
8    trait_name: Ident,
9    request: Request,
10    response: Response,
11}
12
13impl Method {
14    pub fn new(
15        method: prost_build::Method,
16        request_message: syn::ItemStruct,
17        response_message: syn::ItemStruct,
18        config: HttpRule,
19        trait_name: Ident,
20    ) -> Method {
21        let method_name = quote::format_ident!("{}", method.proto_name);
22        // TODO: accept all fields by reference
23        let request = Request::new(request_message, method_name, &config);
24        let response = Response::new(response_message, config.response_body.clone());
25        Method {
26            name: quote::format_ident!("{}", method.name),
27            // method,
28            config,
29            trait_name,
30            request,
31            response,
32        }
33    }
34
35    pub fn method_name(&self) -> Ident {
36        quote::format_ident!("call_{}", self.name)
37    }
38
39    pub fn generate_config(&self) -> TokenStream {
40        let path = self.config.pattern.path();
41        let method_type = quote::format_ident!("{}", self.config.pattern.method());
42        let name = self.method_name();
43        quote::quote!(
44            config.route(#path, ::actix_web::web::#method_type().to(#name));
45        )
46    }
47
48    pub fn generate_route(&self) -> TokenStream {
49        let method_name = self.method_name();
50        let name = &self.name;
51        let trait_name = &self.trait_name;
52        let request_init = self.request.generate_new_request();
53        let extractors = self.request.generate_extractors();
54        let (payload, payload_convert) = if self.request.has_sub(self.request.body()) {
55            (
56                Some(quote::quote!(payload: ::actix_web::web::Payload,)),
57                Some(quote::quote!(let mut payload = payload.into_inner();)),
58            )
59        } else {
60            (None, None)
61        };
62        let response_convert = self.response.generate_convert();
63        quote::quote!(
64            async fn #method_name(
65                service: ::actix_web::web::Data<dyn #trait_name + Sync + Send + 'static>,
66                http_request: ::actix_web::HttpRequest,
67                #payload
68            ) -> Result<impl Responder, ::actix_prost::Error> {
69                #payload_convert
70                #extractors
71                let request = #request_init;
72                let request = ::actix_prost::new_request(request, &http_request);
73                let response = service
74                    .#name(request)
75                    .await?;
76                let headers = response.metadata().clone().into_headers();
77                let response = response.into_inner();
78                #response_convert
79                let mut json_response = ::actix_web::web::Json(response).customize();
80                for (key, value) in headers.iter() {
81                    json_response = json_response.insert_header((key.as_str(), value.as_bytes()));
82                }
83                Ok(json_response)
84            }
85        )
86    }
87
88    pub fn request(&self) -> &Request {
89        &self.request
90    }
91}