open_lark/core/trait_system/
macros.rs1#[macro_export]
29macro_rules! impl_executable_builder {
30 (
31 $builder:ty,
32 $service:ty,
33 $request:ty,
34 $response:ty,
35 $method:ident
36 ) => {
37 #[async_trait::async_trait]
38 impl $crate::core::trait_system::ExecutableBuilder<$service, $request, $response>
39 for $builder
40 {
41 fn build(self) -> $request {
42 self.build()
43 }
44
45 async fn execute(self, service: &$service) -> $crate::core::SDKResult<$response> {
46 service.$method(&self.build(), None).await
47 }
48
49 async fn execute_with_options(
50 self,
51 service: &$service,
52 option: $crate::core::req_option::RequestOption,
53 ) -> $crate::core::SDKResult<$response> {
54 service.$method(&self.build(), Some(option)).await
55 }
56 }
57 };
58}
59
60#[macro_export]
64macro_rules! impl_executable_builder_owned {
65 (
66 $builder:ty,
67 $service:ty,
68 $request:ty,
69 $response:ty,
70 $method:ident
71 ) => {
72 #[async_trait::async_trait]
73 impl $crate::core::trait_system::ExecutableBuilder<$service, $request, $response>
74 for $builder
75 {
76 fn build(self) -> $request {
77 self.build()
78 }
79
80 async fn execute(self, service: &$service) -> $crate::core::SDKResult<$response> {
81 service.$method(self.build(), None).await
82 }
83
84 async fn execute_with_options(
85 self,
86 service: &$service,
87 option: $crate::core::req_option::RequestOption,
88 ) -> $crate::core::SDKResult<$response> {
89 service.$method(self.build(), Some(option)).await
90 }
91 }
92 };
93}
94
95#[macro_export]
99macro_rules! impl_executable_builder_config {
100 (
101 $builder:ty,
102 $request:ty,
103 $response:ty,
104 $function:ident
105 ) => {
106 impl $builder {
107 pub async fn execute(
109 self,
110 config: &$crate::core::config::Config,
111 ) -> $crate::core::SDKResult<$response> {
112 $function(self.build(), config, None).await
113 }
114
115 pub async fn execute_with_options(
117 self,
118 config: &$crate::core::config::Config,
119 option: $crate::core::req_option::RequestOption,
120 ) -> $crate::core::SDKResult<$response> {
121 $function(self.build(), config, Some(option)).await
122 }
123 }
124 };
125}
126
127#[macro_export]
133macro_rules! impl_basic_service {
134 ($service_type:ty, $name:expr, $version:expr) => {
135 impl $crate::core::trait_system::Service for $service_type {
136 fn config(&self) -> &$crate::core::config::Config {
137 &self.config
138 }
139
140 fn service_name() -> &'static str {
141 $name
142 }
143
144 fn service_version() -> &'static str {
145 $version
146 }
147 }
148
149 impl $crate::core::trait_system::ServiceObservability for $service_type {}
150
151 impl $crate::core::trait_system::ServiceBuilder<$service_type> for $service_type {
152 fn build(config: $crate::core::config::Config) -> $service_type {
153 Self { config }
154 }
155 }
156 };
157}
158
159#[macro_export]
161macro_rules! impl_async_service {
162 ($service_type:ty, $request_type:ty, $response_type:ty) => {
163 impl $crate::core::trait_system::AsyncServiceOperation<$request_type, $response_type>
164 for $service_type
165 {
166 }
167 };
168}
169
170#[macro_export]
172macro_rules! impl_service_health_check {
173 ($service_type:ty) => {
174 impl $crate::core::trait_system::ServiceHealthCheck for $service_type {
175 async fn health_check(
176 &self,
177 ) -> $crate::core::SDKResult<$crate::core::trait_system::ServiceHealthStatus> {
178 use $crate::core::trait_system::ServiceHealthStatus;
179
180 if !self.is_config_valid() {
181 return Ok(ServiceHealthStatus::Unhealthy(
182 "Invalid configuration".to_string(),
183 ));
184 }
185
186 Ok(ServiceHealthStatus::Healthy)
188 }
189 }
190 };
191}
192
193#[macro_export]
195macro_rules! impl_configurable_service {
196 ($service_type:ty) => {
197 impl $crate::core::trait_system::ConfigurableService for $service_type {
198 fn update_config(
199 &mut self,
200 new_config: $crate::core::config::Config,
201 ) -> $crate::core::SDKResult<()> {
202 self.validate_config(&new_config)?;
203 self.config = new_config;
204 Ok(())
205 }
206 }
207 };
208}
209
210#[macro_export]
212macro_rules! impl_full_service {
213 ($service_type:ty, $name:expr) => {
214 impl_full_service!($service_type, $name, "v1");
215 };
216 ($service_type:ty, $name:expr, $version:expr) => {
217 $crate::impl_basic_service!($service_type, $name, $version);
218 $crate::impl_service_health_check!($service_type);
219 $crate::impl_configurable_service!($service_type);
220 };
221}
222
223#[macro_export]
225macro_rules! impl_service_constructor {
226 ($service_type:ty) => {
227 impl $service_type {
228 pub fn new(config: $crate::core::config::Config) -> Self {
230 <Self as $crate::core::trait_system::ServiceBuilder<Self>>::build(config)
231 }
232 }
233 };
234}
235
236#[cfg(test)]
237mod tests {
238 use crate::core::{
239 api_resp::{ApiResponseTrait, BaseResponse, RawResponse, ResponseFormat},
240 config::Config,
241 req_option::RequestOption,
242 trait_system::ExecutableBuilder,
243 SDKResult,
244 };
245 use serde::{Deserialize, Serialize};
246
247 #[derive(Debug, Clone)]
249 struct MockRequest {
250 data: String,
251 }
252
253 #[derive(Debug, Serialize, Deserialize)]
254 struct MockResponse {
255 result: String,
256 }
257
258 impl ApiResponseTrait for MockResponse {
259 fn data_format() -> ResponseFormat {
260 ResponseFormat::Data
261 }
262 }
263
264 #[derive(Clone)]
265 struct MockService;
266
267 impl MockService {
268 async fn test_method(
269 &self,
270 request: &MockRequest,
271 _option: Option<RequestOption>,
272 ) -> SDKResult<BaseResponse<MockResponse>> {
273 Ok(BaseResponse {
274 raw_response: RawResponse {
275 code: 0,
276 msg: "success".to_string(),
277 err: None,
278 },
279 data: Some(MockResponse {
280 result: format!("processed: {}", request.data),
281 }),
282 })
283 }
284
285 async fn test_method_owned(
286 &self,
287 request: MockRequest,
288 _option: Option<RequestOption>,
289 ) -> SDKResult<BaseResponse<MockResponse>> {
290 Ok(BaseResponse {
291 raw_response: RawResponse {
292 code: 0,
293 msg: "success".to_string(),
294 err: None,
295 },
296 data: Some(MockResponse {
297 result: format!("owned: {}", request.data),
298 }),
299 })
300 }
301 }
302
303 #[derive(Default)]
304 struct MockRequestBuilder {
305 data: String,
306 }
307
308 impl MockRequestBuilder {
309 pub fn data(mut self, data: impl Into<String>) -> Self {
310 self.data = data.into();
311 self
312 }
313
314 pub fn build(self) -> MockRequest {
315 MockRequest { data: self.data }
316 }
317 }
318
319 #[derive(Default)]
320 struct MockRequestBuilderOwned {
321 data: String,
322 }
323
324 impl MockRequestBuilderOwned {
325 pub fn data(mut self, data: impl Into<String>) -> Self {
326 self.data = data.into();
327 self
328 }
329
330 pub fn build(self) -> MockRequest {
331 MockRequest { data: self.data }
332 }
333 }
334
335 crate::impl_executable_builder!(
337 MockRequestBuilder,
338 MockService,
339 MockRequest,
340 BaseResponse<MockResponse>,
341 test_method
342 );
343
344 crate::impl_executable_builder_owned!(
345 MockRequestBuilderOwned,
346 MockService,
347 MockRequest,
348 BaseResponse<MockResponse>,
349 test_method_owned
350 );
351
352 async fn mock_config_function(
353 request: MockRequest,
354 _config: &Config,
355 _option: Option<RequestOption>,
356 ) -> SDKResult<BaseResponse<MockResponse>> {
357 Ok(BaseResponse {
358 raw_response: RawResponse {
359 code: 0,
360 msg: "success".to_string(),
361 err: None,
362 },
363 data: Some(MockResponse {
364 result: format!("config: {}", request.data),
365 }),
366 })
367 }
368
369 #[derive(Default)]
370 struct MockConfigBuilder {
371 data: String,
372 }
373
374 impl MockConfigBuilder {
375 pub fn data(mut self, data: impl Into<String>) -> Self {
376 self.data = data.into();
377 self
378 }
379
380 pub fn build(self) -> MockRequest {
381 MockRequest { data: self.data }
382 }
383 }
384
385 crate::impl_executable_builder_config!(
386 MockConfigBuilder,
387 MockRequest,
388 BaseResponse<MockResponse>,
389 mock_config_function
390 );
391
392 #[tokio::test]
393 async fn test_executable_builder_macro() {
394 let service = MockService;
395 let builder = MockRequestBuilder::default().data("test data");
396
397 let result = builder.execute(&service).await;
398 assert!(result.is_ok());
399
400 let response = result.unwrap();
401 assert_eq!(response.code(), 0);
402 assert_eq!(
403 response.data.as_ref().unwrap().result,
404 "processed: test data"
405 );
406 }
407
408 #[tokio::test]
409 async fn test_executable_builder_macro_with_options() {
410 let service = MockService;
411 let builder = MockRequestBuilder::default().data("test with options");
412 let option = RequestOption::default();
413
414 let result = builder.execute_with_options(&service, option).await;
415 assert!(result.is_ok());
416
417 let response = result.unwrap();
418 assert_eq!(response.code(), 0);
419 assert_eq!(
420 response.data.as_ref().unwrap().result,
421 "processed: test with options"
422 );
423 }
424
425 #[tokio::test]
426 async fn test_executable_builder_owned_macro() {
427 let service = MockService;
428 let builder = MockRequestBuilderOwned::default().data("owned test");
429
430 let result = builder.execute(&service).await;
431 assert!(result.is_ok());
432
433 let response = result.unwrap();
434 assert_eq!(response.code(), 0);
435 assert_eq!(response.data.as_ref().unwrap().result, "owned: owned test");
436 }
437
438 #[tokio::test]
439 async fn test_executable_builder_config_macro() {
440 let config = Config::default();
441 let builder = MockConfigBuilder::default().data("config test");
442
443 let result = builder.execute(&config).await;
444 assert!(result.is_ok());
445
446 let response = result.unwrap();
447 assert_eq!(response.code(), 0);
448 assert_eq!(
449 response.data.as_ref().unwrap().result,
450 "config: config test"
451 );
452 }
453
454 #[tokio::test]
455 async fn test_executable_builder_config_macro_with_options() {
456 let config = Config::default();
457 let builder = MockConfigBuilder::default().data("config with options");
458 let option = RequestOption::default();
459
460 let result = builder.execute_with_options(&config, option).await;
461 assert!(result.is_ok());
462
463 let response = result.unwrap();
464 assert_eq!(response.code(), 0);
465 assert_eq!(
466 response.data.as_ref().unwrap().result,
467 "config: config with options"
468 );
469 }
470
471 #[test]
472 fn test_builder_construction() {
473 let builder = MockRequestBuilder::default().data("test");
474 let request = builder.build();
475 assert_eq!(request.data, "test");
476 }
477
478 #[test]
479 fn test_builder_chaining() {
480 let builder = MockRequestBuilder::default().data("first").data("second");
481 let request = builder.build();
482 assert_eq!(request.data, "second");
483 }
484
485 #[test]
486 fn test_owned_builder_construction() {
487 let builder = MockRequestBuilderOwned::default().data("owned test");
488 let request = builder.build();
489 assert_eq!(request.data, "owned test");
490 }
491
492 #[test]
493 fn test_config_builder_construction() {
494 let builder = MockConfigBuilder::default().data("config builder test");
495 let request = builder.build();
496 assert_eq!(request.data, "config builder test");
497 }
498
499 #[test]
500 fn test_mock_response_api_trait() {
501 let format = MockResponse::data_format();
502 assert!(matches!(format, ResponseFormat::Data));
503 }
504
505 #[test]
506 fn test_mock_response_serialization() {
507 let response = MockResponse {
508 result: "test result".to_string(),
509 };
510
511 let serialized = serde_json::to_string(&response).expect("Should serialize");
512 let deserialized: MockResponse =
513 serde_json::from_str(&serialized).expect("Should deserialize");
514
515 assert_eq!(response.result, deserialized.result);
516 }
517
518 #[test]
519 fn test_mock_request_debug() {
520 let request = MockRequest {
521 data: "debug test".to_string(),
522 };
523
524 let debug_str = format!("{:?}", request);
525 assert!(debug_str.contains("MockRequest"));
526 assert!(debug_str.contains("debug test"));
527 }
528
529 #[test]
530 fn test_mock_request_clone() {
531 let request = MockRequest {
532 data: "clone test".to_string(),
533 };
534
535 let cloned = request.clone();
536 assert_eq!(request.data, cloned.data);
537 }
538
539 #[test]
540 fn test_builder_with_empty_data() {
541 let builder = MockRequestBuilder::default().data("");
542 let request = builder.build();
543 assert_eq!(request.data, "");
544 }
545
546 #[test]
547 fn test_builder_with_unicode_data() {
548 let builder = MockRequestBuilder::default().data("测试数据 🚀");
549 let request = builder.build();
550 assert_eq!(request.data, "测试数据 🚀");
551 }
552
553 #[test]
554 fn test_builder_with_long_data() {
555 let long_data = "a".repeat(10000);
556 let builder = MockRequestBuilder::default().data(&long_data);
557 let request = builder.build();
558 assert_eq!(request.data, long_data);
559 }
560}