1use super::{Attributes, Method, Service};
2use crate::{generate_doc_comment, generate_doc_comments, naive_snake_case};
3use proc_macro2::{Span, TokenStream};
4use quote::quote;
5use syn::{Ident, Lit, LitStr};
6
7pub fn generate<T: Service>(
12 service: &T,
13 emit_package: bool,
14 proto_path: &str,
15 compile_well_known_types: bool,
16 attributes: &Attributes,
17) -> TokenStream {
18 let methods = generate_methods(service, proto_path, compile_well_known_types);
19
20 let server_service = quote::format_ident!("{}Server", service.name());
21 let server_trait = quote::format_ident!("{}", service.name());
22 let server_mod = quote::format_ident!("{}_server", naive_snake_case(&service.name()));
23 let generated_trait = generate_trait(
24 service,
25 proto_path,
26 compile_well_known_types,
27 server_trait.clone(),
28 );
29 let service_doc = generate_doc_comments(service.comment());
30 let package = if emit_package { service.package() } else { "" };
31 let path = format!(
33 "{}{}{}",
34 package,
35 if package.is_empty() { "" } else { "." },
36 service.identifier()
37 );
38 let transport = generate_transport(&server_service, &server_trait, &path);
39 let mod_attributes = attributes.for_mod(package);
40 let struct_attributes = attributes.for_struct(&path);
41
42 let compression_enabled = cfg!(feature = "compression");
43
44 let compression_config_ty = if compression_enabled {
45 quote! { EnabledCompressionEncodings }
46 } else {
47 quote! { () }
48 };
49
50 let configure_compression_methods = if compression_enabled {
51 quote! {
52 pub fn accept_gzip(mut self) -> Self {
54 self.accept_compression_encodings.enable_gzip();
55 self
56 }
57
58 pub fn send_gzip(mut self) -> Self {
60 self.send_compression_encodings.enable_gzip();
61 self
62 }
63 }
64 } else {
65 quote! {}
66 };
67
68 quote! {
69 #(#mod_attributes)*
71 pub mod #server_mod {
72 #![allow(
73 unused_variables,
74 dead_code,
75 missing_docs,
76 clippy::let_unit_value,
78 )]
79 use tonic::codegen::*;
80
81 #generated_trait
82
83 #service_doc
84 #(#struct_attributes)*
85 #[derive(Debug)]
86 pub struct #server_service<T: #server_trait> {
87 inner: _Inner<T>,
88 accept_compression_encodings: #compression_config_ty,
89 send_compression_encodings: #compression_config_ty,
90 }
91
92 struct _Inner<T>(Arc<T>);
93
94 impl<T: #server_trait> #server_service<T> {
95 pub fn new(inner: T) -> Self {
96 let inner = Arc::new(inner);
97 let inner = _Inner(inner);
98 Self {
99 inner,
100 accept_compression_encodings: Default::default(),
101 send_compression_encodings: Default::default(),
102 }
103 }
104
105 pub fn with_interceptor<F>(inner: T, interceptor: F) -> InterceptedService<Self, F>
106 where
107 F: tonic::service::Interceptor,
108 {
109 InterceptedService::new(Self::new(inner), interceptor)
110 }
111
112 #configure_compression_methods
113 }
114
115 impl<T, B> tonic::codegen::Service<http::Request<B>> for #server_service<T>
116 where
117 T: #server_trait,
118 B: Body + Send + Sync + 'static,
119 B::Error: Into<StdError> + Send + 'static,
120 {
121 type Response = http::Response<tonic::body::BoxBody>;
122 type Error = Never;
123 type Future = BoxFuture<Self::Response, Self::Error>;
124
125 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
126 Poll::Ready(Ok(()))
127 }
128
129 fn call(&mut self, req: http::Request<B>) -> Self::Future {
130 let inner = self.inner.clone();
131
132 match req.uri().path() {
133 #methods
134
135 _ => Box::pin(async move {
136 Ok(http::Response::builder()
137 .status(200)
138 .header("grpc-status", "12")
139 .header("content-type", "application/grpc")
140 .body(empty_body())
141 .unwrap())
142 }),
143 }
144 }
145 }
146
147 impl<T: #server_trait> Clone for #server_service<T> {
148 fn clone(&self) -> Self {
149 let inner = self.inner.clone();
150 Self {
151 inner,
152 accept_compression_encodings: self.accept_compression_encodings,
153 send_compression_encodings: self.send_compression_encodings,
154 }
155 }
156 }
157
158 impl<T: #server_trait> Clone for _Inner<T> {
159 fn clone(&self) -> Self {
160 Self(self.0.clone())
161 }
162 }
163
164 impl<T: std::fmt::Debug> std::fmt::Debug for _Inner<T> {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(f, "{:?}", self.0)
167 }
168 }
169
170 #transport
171 }
172 }
173}
174
175fn generate_trait<T: Service>(
176 service: &T,
177 proto_path: &str,
178 compile_well_known_types: bool,
179 server_trait: Ident,
180) -> TokenStream {
181 let methods = generate_trait_methods(service, proto_path, compile_well_known_types);
182 let trait_doc = generate_doc_comment(&format!(
183 "Generated trait containing gRPC methods that should be implemented for use with {}Server.",
184 service.name()
185 ));
186
187 quote! {
188 #trait_doc
189 #[async_trait]
190 pub trait #server_trait : Send + Sync + 'static {
191 #methods
192 }
193 }
194}
195
196fn generate_trait_methods<T: Service>(
197 service: &T,
198 proto_path: &str,
199 compile_well_known_types: bool,
200) -> TokenStream {
201 let mut stream = TokenStream::new();
202
203 for method in service.methods() {
204 let name = quote::format_ident!("{}", method.name());
205
206 let (req_message, res_message) =
207 method.request_response_name(proto_path, compile_well_known_types);
208
209 let method_doc = generate_doc_comments(method.comment());
210
211 let method = match (method.client_streaming(), method.server_streaming()) {
212 (false, false) => {
213 quote! {
214 #method_doc
215 async fn #name(&self, request: tonic::Request<#req_message>)
216 -> Result<tonic::Response<#res_message>, tonic::Status>;
217 }
218 }
219 (true, false) => {
220 quote! {
221 #method_doc
222 async fn #name(&self, request: tonic::Request<tonic::Streaming<#req_message>>)
223 -> Result<tonic::Response<#res_message>, tonic::Status>;
224 }
225 }
226 (false, true) => {
227 let stream = quote::format_ident!("{}Stream", method.identifier());
228 let stream_doc = generate_doc_comment(&format!(
229 "Server streaming response type for the {} method.",
230 method.identifier()
231 ));
232
233 quote! {
234 #stream_doc
235 type #stream: futures_core::Stream<Item = Result<#res_message, tonic::Status>> + Send + Sync + 'static;
236
237 #method_doc
238 async fn #name(&self, request: tonic::Request<#req_message>)
239 -> Result<tonic::Response<Self::#stream>, tonic::Status>;
240 }
241 }
242 (true, true) => {
243 let stream = quote::format_ident!("{}Stream", method.identifier());
244 let stream_doc = generate_doc_comment(&format!(
245 "Server streaming response type for the {} method.",
246 method.identifier()
247 ));
248
249 quote! {
250 #stream_doc
251 type #stream: futures_core::Stream<Item = Result<#res_message, tonic::Status>> + Send + Sync + 'static;
252
253 #method_doc
254 async fn #name(&self, request: tonic::Request<tonic::Streaming<#req_message>>)
255 -> Result<tonic::Response<Self::#stream>, tonic::Status>;
256 }
257 }
258 };
259
260 stream.extend(method);
261 }
262
263 stream
264}
265
266#[cfg(feature = "transport")]
267fn generate_transport(
268 server_service: &syn::Ident,
269 server_trait: &syn::Ident,
270 service_name: &str,
271) -> TokenStream {
272 let service_name = syn::LitStr::new(service_name, proc_macro2::Span::call_site());
273
274 quote! {
275 impl<T: #server_trait> tonic::transport::NamedService for #server_service<T> {
276 const NAME: &'static str = #service_name;
277 }
278 }
279}
280
281#[cfg(not(feature = "transport"))]
282fn generate_transport(
283 _server_service: &syn::Ident,
284 _server_trait: &syn::Ident,
285 _service_name: &str,
286) -> TokenStream {
287 TokenStream::new()
288}
289
290fn generate_methods<T: Service>(
291 service: &T,
292 proto_path: &str,
293 compile_well_known_types: bool,
294) -> TokenStream {
295 let mut stream = TokenStream::new();
296
297 for method in service.methods() {
298 let path = format!(
299 "/{}{}{}/{}",
300 service.package(),
301 if service.package().is_empty() {
302 ""
303 } else {
304 "."
305 },
306 service.identifier(),
307 method.identifier()
308 );
309 let method_path = Lit::Str(LitStr::new(&path, Span::call_site()));
310 let ident = quote::format_ident!("{}", method.name());
311 let server_trait = quote::format_ident!("{}", service.name());
312
313 let method_stream = match (method.client_streaming(), method.server_streaming()) {
314 (false, false) => generate_unary(
315 method,
316 proto_path,
317 compile_well_known_types,
318 ident,
319 server_trait,
320 ),
321
322 (false, true) => generate_server_streaming(
323 method,
324 proto_path,
325 compile_well_known_types,
326 ident.clone(),
327 server_trait,
328 ),
329 (true, false) => generate_client_streaming(
330 method,
331 proto_path,
332 compile_well_known_types,
333 ident.clone(),
334 server_trait,
335 ),
336
337 (true, true) => generate_streaming(
338 method,
339 proto_path,
340 compile_well_known_types,
341 ident.clone(),
342 server_trait,
343 ),
344 };
345
346 let method = quote! {
347 #method_path => {
348 #method_stream
349 }
350 };
351 stream.extend(method);
352 }
353
354 stream
355}
356
357fn generate_unary<T: Method>(
358 method: &T,
359 proto_path: &str,
360 compile_well_known_types: bool,
361 method_ident: Ident,
362 server_trait: Ident,
363) -> TokenStream {
364 let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
365
366 let service_ident = quote::format_ident!("{}Svc", method.identifier());
367
368 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
369
370 quote! {
371 #[allow(non_camel_case_types)]
372 struct #service_ident<T: #server_trait >(pub Arc<T>);
373
374 impl<T: #server_trait> tonic::server::UnaryService<#request> for #service_ident<T> {
375 type Response = #response;
376 type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
377
378 fn call(&mut self, request: tonic::Request<#request>) -> Self::Future {
379 let inner = self.0.clone();
380 let fut = async move {
381 (*inner).#method_ident(request).await
382 };
383 Box::pin(fut)
384 }
385 }
386
387 let accept_compression_encodings = self.accept_compression_encodings;
388 let send_compression_encodings = self.send_compression_encodings;
389 let inner = self.inner.clone();
390 let fut = async move {
391 let inner = inner.0;
392 let method = #service_ident(inner);
393 let codec = #codec_name::default();
394
395 let mut grpc = tonic::server::Grpc::new(codec)
396 .apply_compression_config(accept_compression_encodings, send_compression_encodings);
397
398 let res = grpc.unary(method, req).await;
399 Ok(res)
400 };
401
402 Box::pin(fut)
403 }
404}
405
406fn generate_server_streaming<T: Method>(
407 method: &T,
408 proto_path: &str,
409 compile_well_known_types: bool,
410 method_ident: Ident,
411 server_trait: Ident,
412) -> TokenStream {
413 let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
414
415 let service_ident = quote::format_ident!("{}Svc", method.identifier());
416
417 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
418
419 let response_stream = quote::format_ident!("{}Stream", method.identifier());
420
421 quote! {
422 #[allow(non_camel_case_types)]
423 struct #service_ident<T: #server_trait >(pub Arc<T>);
424
425 impl<T: #server_trait> tonic::server::ServerStreamingService<#request> for #service_ident<T> {
426 type Response = #response;
427 type ResponseStream = T::#response_stream;
428 type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
429
430 fn call(&mut self, request: tonic::Request<#request>) -> Self::Future {
431 let inner = self.0.clone();
432 let fut = async move {
433 (*inner).#method_ident(request).await
434 };
435 Box::pin(fut)
436 }
437 }
438
439 let accept_compression_encodings = self.accept_compression_encodings;
440 let send_compression_encodings = self.send_compression_encodings;
441 let inner = self.inner.clone();
442 let fut = async move {
443 let inner = inner.0;
444 let method = #service_ident(inner);
445 let codec = #codec_name::default();
446
447 let mut grpc = tonic::server::Grpc::new(codec)
448 .apply_compression_config(accept_compression_encodings, send_compression_encodings);
449
450 let res = grpc.server_streaming(method, req).await;
451 Ok(res)
452 };
453
454 Box::pin(fut)
455 }
456}
457
458fn generate_client_streaming<T: Method>(
459 method: &T,
460 proto_path: &str,
461 compile_well_known_types: bool,
462 method_ident: Ident,
463 server_trait: Ident,
464) -> TokenStream {
465 let service_ident = quote::format_ident!("{}Svc", method.identifier());
466
467 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
468 let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
469
470 quote! {
471 #[allow(non_camel_case_types)]
472 struct #service_ident<T: #server_trait >(pub Arc<T>);
473
474 impl<T: #server_trait> tonic::server::ClientStreamingService<#request> for #service_ident<T>
475 {
476 type Response = #response;
477 type Future = BoxFuture<tonic::Response<Self::Response>, tonic::Status>;
478
479 fn call(&mut self, request: tonic::Request<tonic::Streaming<#request>>) -> Self::Future {
480 let inner = self.0.clone();
481 let fut = async move {
482 (*inner).#method_ident(request).await
483
484 };
485 Box::pin(fut)
486 }
487 }
488
489 let accept_compression_encodings = self.accept_compression_encodings;
490 let send_compression_encodings = self.send_compression_encodings;
491 let inner = self.inner.clone();
492 let fut = async move {
493 let inner = inner.0;
494 let method = #service_ident(inner);
495 let codec = #codec_name::default();
496
497 let mut grpc = tonic::server::Grpc::new(codec)
498 .apply_compression_config(accept_compression_encodings, send_compression_encodings);
499
500 let res = grpc.client_streaming(method, req).await;
501 Ok(res)
502 };
503
504 Box::pin(fut)
505 }
506}
507
508fn generate_streaming<T: Method>(
509 method: &T,
510 proto_path: &str,
511 compile_well_known_types: bool,
512 method_ident: Ident,
513 server_trait: Ident,
514) -> TokenStream {
515 let codec_name = syn::parse_str::<syn::Path>(T::CODEC_PATH).unwrap();
516
517 let service_ident = quote::format_ident!("{}Svc", method.identifier());
518
519 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
520
521 let response_stream = quote::format_ident!("{}Stream", method.identifier());
522
523 quote! {
524 #[allow(non_camel_case_types)]
525 struct #service_ident<T: #server_trait>(pub Arc<T>);
526
527 impl<T: #server_trait> tonic::server::StreamingService<#request> for #service_ident<T>
528 {
529 type Response = #response;
530 type ResponseStream = T::#response_stream;
531 type Future = BoxFuture<tonic::Response<Self::ResponseStream>, tonic::Status>;
532
533 fn call(&mut self, request: tonic::Request<tonic::Streaming<#request>>) -> Self::Future {
534 let inner = self.0.clone();
535 let fut = async move {
536 (*inner).#method_ident(request).await
537 };
538 Box::pin(fut)
539 }
540 }
541
542 let accept_compression_encodings = self.accept_compression_encodings;
543 let send_compression_encodings = self.send_compression_encodings;
544 let inner = self.inner.clone();
545 let fut = async move {
546 let inner = inner.0;
547 let method = #service_ident(inner);
548 let codec = #codec_name::default();
549
550 let mut grpc = tonic::server::Grpc::new(codec)
551 .apply_compression_config(accept_compression_encodings, send_compression_encodings);
552
553 let res = grpc.streaming(method, req).await;
554 Ok(res)
555 };
556
557 Box::pin(fut)
558 }
559}