open_lark/core/trait_system/
macros.rs

1/// 核心宏:为Builder类型自动实现ExecutableBuilder trait
2///
3/// 这个宏消除了手动实现重复execute方法的需要,
4/// 通过声明式配置自动生成trait实现。
5///
6/// # 参数
7/// - `$builder`: Builder类型名称
8/// - `$service`: 服务类型名称  
9/// - `$request`: 请求类型名称
10/// - `$response`: 响应类型名称
11/// - `$method`: 服务方法名称
12///
13/// # 生成的代码
14/// 为指定的Builder类型实现ExecutableBuilder trait,包括:
15/// - `execute()` 方法:调用 `service.$method(self.build(), None)`
16/// - `execute_with_options()` 方法:调用 `service.$method(self.build(), Some(option))`
17///
18/// # Example
19/// ```rust,ignore
20/// impl_executable_builder!(
21///     UploadMediaRequestBuilder,
22///     MediaService,
23///     UploadMediaRequest,
24///     BaseResponse<UploadMediaRespData>,
25///     upload_all
26/// );
27/// ```
28#[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/// 为使用值类型参数的Builder实现ExecutableBuilder trait
61///
62/// 与主宏的差异:服务方法接受值类型而不是引用类型的Request
63#[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/// 为直接使用Config参数的独立函数实现ExecutableBuilder trait
96///
97/// 这个宏用于那些不通过服务而是直接调用独立函数的Builder类型
98#[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            /// 执行请求
108            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            /// 执行请求(带选项)
116            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// Service trait 相关宏
128
129/// 为基础服务生成标准实现的宏
130///
131/// 这个宏减少了创建简单服务时的样板代码
132#[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/// 为服务生成异步操作支持的宏
160#[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/// 为服务生成健康检查实现的宏
171#[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                // 基础健康检查 - 可以在具体服务中重写
187                Ok(ServiceHealthStatus::Healthy)
188            }
189        }
190    };
191}
192
193/// 为服务生成可配置实现的宏
194#[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/// 一次性实现所有基础服务 traits 的便利宏
211#[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/// 为服务 builder 生成构造函数的宏
224#[macro_export]
225macro_rules! impl_service_constructor {
226    ($service_type:ty) => {
227        impl $service_type {
228            /// 创建服务实例
229            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    // Test types for macro validation
248    #[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    // Use the macro to implement the trait
336    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}