1use super::{generate_doc_comments, naive_snake_case, Attributes};
19use crate::{Method, Service};
20use proc_macro2::TokenStream;
21use quote::{format_ident, quote};
22
23pub fn generate<T: Service>(
28 service: &T,
29 emit_package: bool,
30 proto_path: &str,
31 compile_well_known_types: bool,
32 attributes: &Attributes,
33) -> TokenStream {
34 let service_ident = quote::format_ident!("{}Client", service.name());
35 let client_mod = quote::format_ident!("{}_client", naive_snake_case(service.name()));
36 let methods = generate_methods(service, emit_package, proto_path, compile_well_known_types);
37
38 let service_doc = generate_doc_comments(service.comment());
39
40 let package = if emit_package { service.package() } else { "" };
41 let path = format!(
42 "{}{}{}",
43 package,
44 if package.is_empty() { "" } else { "." },
45 service.identifier()
46 );
47
48 let mod_attributes = attributes.for_mod(package);
49 let struct_attributes = attributes.for_struct(&path);
50
51 quote! {
52 #(#mod_attributes)*
54 pub mod #client_mod {
55 #![allow(
56 unused_variables,
57 dead_code,
58 missing_docs,
59 clippy::let_unit_value,
61 )]
62 use dubbo::codegen::*;
63
64 #service_doc
65 #(#struct_attributes)*
66 #[derive(Clone)]
67 pub struct #service_ident {
68 inner: TripleClient,
69 }
70
71 impl #service_ident {
72 pub fn connect(host: String) -> Self {
73 let cli = TripleClient::connect(host);
74 #service_ident {
75 inner: cli,
76 }
77 }
78
79 pub fn new(builder: ClientBuilder) -> Self {
80 Self {
81 inner: TripleClient::new(builder),
82 }
83 }
84
85 #methods
86
87 }
88 }
89 }
90}
91
92fn generate_methods<T: Service>(
93 service: &T,
94 emit_package: bool,
95 proto_path: &str,
96 compile_well_known_types: bool,
97) -> TokenStream {
98 let mut stream = TokenStream::new();
99 let package = if emit_package { service.package() } else { "" };
100
101 for method in service.methods() {
102 let service_unique_name = format!(
103 "{}{}{}",
104 package,
105 if package.is_empty() { "" } else { "." },
106 service.identifier()
107 );
108 let path = format!(
109 "/{}{}{}/{}",
110 package,
111 if package.is_empty() { "" } else { "." },
112 service.identifier(),
113 method.identifier()
114 );
115
116 stream.extend(generate_doc_comments(method.comment()));
117
118 let method = match (method.client_streaming(), method.server_streaming()) {
119 (false, false) => generate_unary(
120 service_unique_name,
121 &method,
122 proto_path,
123 compile_well_known_types,
124 path,
125 ),
126 (false, true) => generate_server_streaming(
127 service_unique_name,
128 &method,
129 proto_path,
130 compile_well_known_types,
131 path,
132 ),
133 (true, false) => generate_client_streaming(
134 service_unique_name,
135 &method,
136 proto_path,
137 compile_well_known_types,
138 path,
139 ),
140 (true, true) => generate_streaming(
141 service_unique_name,
142 &method,
143 proto_path,
144 compile_well_known_types,
145 path,
146 ),
147 };
148
149 stream.extend(method);
150 }
151
152 stream
153}
154
155fn generate_unary<T: Method>(
156 service_unique_name: String,
157 method: &T,
158 proto_path: &str,
159 compile_well_known_types: bool,
160 path: String,
161) -> TokenStream {
162 let ident = format_ident!("{}", method.name());
163 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
164 let method_name = method.identifier();
165
166 quote! {
167 pub async fn #ident(
168 &mut self,
169 request: Request<#request>,
170 ) -> Result<Response<#response>, dubbo::status::Status> {
171 let invocation = RpcInvocation::default()
172 .with_service_unique_name(String::from(#service_unique_name))
173 .with_method_name(String::from(#method_name));
174 let path = http::uri::PathAndQuery::from_static(#path);
175 self.inner.unary(
176 request,
177 path,
178 invocation,
179 ).await
180 }
181 }
182}
183
184fn generate_server_streaming<T: Method>(
185 service_unique_name: String,
186 method: &T,
187 proto_path: &str,
188 compile_well_known_types: bool,
189 path: String,
190) -> TokenStream {
191 let ident = format_ident!("{}", method.name());
192 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
193 let method_name = method.identifier();
194
195 quote! {
196 pub async fn #ident(
197 &mut self,
198 request: Request<#request>,
199 ) -> Result<Response<Decoding<#response>>, dubbo::status::Status> {
200 let invocation = RpcInvocation::default()
201 .with_service_unique_name(String::from(#service_unique_name))
202 .with_method_name(String::from(#method_name));
203 let path = http::uri::PathAndQuery::from_static(#path);
204 self.inner.server_streaming(
205 request,
206 path,
207 invocation,
208 ).await
209 }
210 }
211}
212
213fn generate_client_streaming<T: Method>(
214 service_unique_name: String,
215 method: &T,
216 proto_path: &str,
217 compile_well_known_types: bool,
218 path: String,
219) -> TokenStream {
220 let ident = format_ident!("{}", method.name());
221 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
222 let method_name = method.identifier();
223
224 quote! {
225 pub async fn #ident(
226 &mut self,
227 request: impl IntoStreamingRequest<Message = #request>
228 ) -> Result<Response<#response>, dubbo::status::Status> {
229 let invocation = RpcInvocation::default()
230 .with_service_unique_name(String::from(#service_unique_name))
231 .with_method_name(String::from(#method_name));
232 let path = http::uri::PathAndQuery::from_static(#path);
233 self.inner.client_streaming(
234 request,
235 path,
236 invocation,
237 ).await
238 }
239 }
240}
241
242fn generate_streaming<T: Method>(
243 service_unique_name: String,
244 method: &T,
245 proto_path: &str,
246 compile_well_known_types: bool,
247 path: String,
248) -> TokenStream {
249 let ident = format_ident!("{}", method.name());
250 let (request, response) = method.request_response_name(proto_path, compile_well_known_types);
251 let method_name = method.identifier();
252
253 quote! {
254 pub async fn #ident(
255 &mut self,
256 request: impl IntoStreamingRequest<Message = #request>
257 ) -> Result<Response<Decoding<#response>>, dubbo::status::Status> {
258 let invocation = RpcInvocation::default()
259 .with_service_unique_name(String::from(#service_unique_name))
260 .with_method_name(String::from(#method_name));
261 let path = http::uri::PathAndQuery::from_static(#path);
262 self.inner.bidi_streaming(
263 request,
264 path,
265 invocation,
266 ).await
267 }
268 }
269}