use proc_macro2::TokenStream;
use quote::quote;
use syn::Type;
use crate::utils::MethodInfo;
use crate::utils::to_pascal_case;
pub fn generate_native_to_proto_request_unary(request_type: &Type) -> TokenStream {
let _ = request_type;
quote! {}
}
pub fn generate_native_to_proto_request_streaming(request_type: &Type) -> TokenStream {
let _ = request_type;
quote! {}
}
pub fn generate_proto_to_native_response(_response_type: &Type) -> TokenStream {
quote! { Ok(response) }
}
pub fn generate_request_proto_type(request_type: &Type) -> TokenStream {
quote! { #request_type }
}
pub fn generate_response_proto_type(response_type: &Type) -> TokenStream {
quote! { #response_type }
}
pub fn generate_route_path(package_name: &str, trait_name: &syn::Ident, method_name: &syn::Ident) -> String {
format!("/{}.{}/{}", package_name, trait_name, to_pascal_case(&method_name.to_string()))
}
pub fn generate_codec_init(encode: TokenStream, decode: TokenStream, mode: Option<TokenStream>) -> TokenStream {
if let Some(mode) = mode {
quote! { let codec = ::proto_rs::ProtoCodec::<#encode, #decode, #mode>::default(); }
} else {
quote! { let codec = ::proto_rs::ProtoCodec::<#encode, #decode>::default(); }
}
}
pub fn generate_stream_conversion(_inner_response_type: &Type) -> TokenStream {
quote! { Ok(response) }
}
pub fn is_streaming_method(method: &MethodInfo) -> bool {
method.is_streaming
}
pub fn generate_ready_check() -> TokenStream {
quote! {
self.inner
.ready()
.await
.map_err(|e| tonic::Status::unknown(format!("Service was not ready: {}", e.into())))?;
}
}
pub fn client_module_name(trait_name: &syn::Ident) -> syn::Ident {
use crate::utils::to_snake_case;
syn::Ident::new(&format!("{}_client", to_snake_case(&trait_name.to_string())), trait_name.span())
}
pub fn server_module_name(trait_name: &syn::Ident) -> syn::Ident {
use crate::utils::to_snake_case;
syn::Ident::new(&format!("{}_server", to_snake_case(&trait_name.to_string())), trait_name.span())
}
pub fn client_struct_name(trait_name: &syn::Ident) -> syn::Ident {
syn::Ident::new(&format!("{trait_name}Client"), trait_name.span())
}
pub fn server_struct_name(trait_name: &syn::Ident) -> syn::Ident {
syn::Ident::new(&format!("{trait_name}Server"), trait_name.span())
}
pub fn generate_service_struct_fields() -> TokenStream {
quote! {
inner: ::proto_rs::alloc::sync::Arc<T>,
accept_compression_encodings: EnabledCompressionEncodings,
send_compression_encodings: EnabledCompressionEncodings,
max_decoding_message_size: Option<usize>,
max_encoding_message_size: Option<usize>,
}
}
pub fn generate_service_constructors() -> TokenStream {
quote! {
pub fn new(inner: T) -> Self {
Self::from_arc(::proto_rs::alloc::sync::Arc::new(inner))
}
pub fn from_arc(inner: ::proto_rs::alloc::sync::Arc<T>) -> Self {
Self {
inner,
accept_compression_encodings: Default::default(),
send_compression_encodings: Default::default(),
max_decoding_message_size: None,
max_encoding_message_size: None,
}
}
}
}
pub fn generate_client_with_interceptor(client_struct: &syn::Ident, has_ctx: bool) -> TokenStream {
let return_ty = if has_ctx {
quote! { #client_struct<InterceptedService<T, F>, Ctx> }
} else {
quote! { #client_struct<InterceptedService<T, F>> }
};
quote! {
pub fn with_interceptor<F>(
inner: T,
interceptor: F,
) -> #return_ty
where
F: tonic::service::Interceptor,
T::ResponseBody: Default,
T: tonic::codegen::Service<http::Request<tonic::body::Body>, Response = http::Response<<T as tonic::client::GrpcService<tonic::body::Body>>::ResponseBody>>,
<T as tonic::codegen::Service<http::Request<tonic::body::Body>>>::Error: Into<StdError> + ::core::marker::Send + ::core::marker::Sync,
{
#client_struct::new(InterceptedService::new(inner, interceptor))
}
}
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
use super::*;
#[test]
fn test_generate_route_path() {
let trait_name: syn::Ident = parse_quote! { TestService };
let method_name: syn::Ident = parse_quote! { test_method };
let path = generate_route_path("my_package", &trait_name, &method_name);
assert_eq!(path, "/my_package.TestService/TestMethod");
}
#[test]
fn test_module_naming() {
let trait_name: syn::Ident = parse_quote! { TestService };
let client_mod = client_module_name(&trait_name);
assert_eq!(client_mod.to_string(), "test_service_client");
let server_mod = server_module_name(&trait_name);
assert_eq!(server_mod.to_string(), "test_service_server");
}
#[test]
fn test_proto_type_generation() {
let ty: Type = parse_quote! { MyRequest };
let proto_type = generate_request_proto_type(&ty);
let expected = quote! { MyRequest };
assert_eq!(proto_type.to_string(), expected.to_string());
}
}