open_lark/service/im/
mod.rs

1//! 即时消息(IM)服务
2//!
3//! 提供飞书即时消息相关的所有API功能,包括消息发送、接收、管理等。
4//! 支持多种消息类型:文本、富文本、图片、文件、卡片等。
5//!
6//! # API版本
7//!
8//! - **v1**: 稳定版本,包含核心消息功能
9//! - **v2**: 新版本,包含增强功能
10//!
11//! # 主要功能
12//!
13//! - 📨 消息发送和接收
14//! - 🎨 富文本和卡片消息
15//! - 📁 文件和媒体消息
16//! - 👥 群聊管理
17//! - 🔔 消息推送和通知
18//!
19//! # 快速开始
20//!
21//! ```rust
22//! use open_lark::prelude::*;
23//!
24//! let client = LarkClient::builder("app_id", "app_secret")
25//!     .with_app_type(AppType::SelfBuild)
26//!     .build();
27//!
28//! // 发送文本消息
29//! let message = CreateMessageRequestBody::builder()
30//!     .receive_id("ou_xxx")
31//!     .msg_type("text")
32//!     .content("{\"text\":\"Hello!\"}")
33//!     .build();
34//!
35//! let request = CreateMessageRequest::builder()
36//!     .receive_id_type("open_id")
37//!     .request_body(message)
38//!     .build();
39//!
40//! // let result = client.im.v1.message.create(request, None).await?;
41//! ```
42
43use crate::{
44    core::config::Config,
45    service::im::{v1::V1, v2::V2},
46};
47
48/// IM API v1版本
49pub mod v1;
50/// IM API v2版本
51pub mod v2;
52
53/// 即时消息服务
54///
55/// 聚合所有IM相关的API版本,提供统一的访问接口。
56/// 通过不同版本的子服务访问具体的API功能。
57pub struct ImService {
58    /// IM API v1版本服务
59    pub v1: V1,
60    /// IM API v2版本服务
61    pub v2: V2,
62}
63
64impl ImService {
65    /// 创建新的IM服务实例
66    ///
67    /// # 参数
68    /// - `config`: 客户端配置
69    pub fn new(config: Config) -> Self {
70        Self {
71            v1: V1::new(config.clone()),
72            v2: V2::new(config),
73        }
74    }
75
76    /// 使用共享配置(实验性)
77    pub fn new_from_shared(shared: std::sync::Arc<Config>) -> Self {
78        Self {
79            v1: V1::new(shared.as_ref().clone()),
80            v2: V2::new(shared.as_ref().clone()),
81        }
82    }
83}
84
85#[cfg(test)]
86#[allow(unused_variables, unused_unsafe)]
87mod tests {
88    use super::*;
89    use crate::core::config::Config;
90
91    fn create_test_config() -> Config {
92        Config::default()
93    }
94
95    #[test]
96    fn test_im_service_creation() {
97        let config = create_test_config();
98        let im_service = ImService::new(config);
99
100        // Verify service structure
101    }
102
103    #[test]
104    fn test_im_service_with_custom_config() {
105        let config = Config::builder()
106            .app_id("im_app")
107            .app_secret("im_secret")
108            .req_timeout(std::time::Duration::from_millis(12000))
109            .base_url("https://im.api.com")
110            .build();
111
112        let im_service = ImService::new(config);
113
114        // Verify service creation with custom config
115    }
116
117    #[test]
118    fn test_im_service_api_versions_independence() {
119        let config = create_test_config();
120        let im_service = ImService::new(config);
121
122        // Test that API versions are independent
123        let v1_ptr = std::ptr::addr_of!(im_service.v1) as *const _;
124        let v2_ptr = std::ptr::addr_of!(im_service.v2) as *const _;
125
126        assert_ne!(v1_ptr, v2_ptr, "API versions should be independent");
127    }
128
129    #[test]
130    fn test_im_service_with_various_configurations() {
131        let test_configs = vec![
132            Config::builder()
133                .app_id("im_basic")
134                .app_secret("basic_secret")
135                .build(),
136            Config::builder()
137                .app_id("im_timeout")
138                .app_secret("timeout_secret")
139                .req_timeout(std::time::Duration::from_millis(18000))
140                .build(),
141            Config::builder()
142                .app_id("im_custom")
143                .app_secret("custom_secret")
144                .base_url("https://custom.im.com")
145                .build(),
146            Config::builder()
147                .app_id("im_full")
148                .app_secret("full_secret")
149                .req_timeout(std::time::Duration::from_millis(22000))
150                .base_url("https://full.im.com")
151                .enable_token_cache(false)
152                .build(),
153        ];
154
155        for config in test_configs {
156            let im_service = ImService::new(config);
157
158            // Each configuration should create valid services
159        }
160    }
161
162    #[test]
163    fn test_im_service_multiple_instances() {
164        let config1 = create_test_config();
165        let config2 = Config::builder()
166            .app_id("im2")
167            .app_secret("secret2")
168            .build();
169
170        let im_service1 = ImService::new(config1);
171        let im_service2 = ImService::new(config2);
172
173        // Services should be independent instances
174        let service1_ptr = std::ptr::addr_of!(im_service1) as *const _;
175        let service2_ptr = std::ptr::addr_of!(im_service2) as *const _;
176
177        assert_ne!(
178            service1_ptr, service2_ptr,
179            "Services should be independent instances"
180        );
181
182        // Each service should have valid API versions
183    }
184
185    #[test]
186    fn test_im_service_config_cloning_behavior() {
187        let original_config = create_test_config();
188
189        // Test that the service works with cloned configs
190        let im_service1 = ImService::new(original_config.clone());
191        let im_service2 = ImService::new(original_config);
192
193        // Both should work independently
194
195        // But should be different service instances
196        let service1_ptr = std::ptr::addr_of!(im_service1) as *const u8;
197        let service2_ptr = std::ptr::addr_of!(im_service2) as *const u8;
198        assert_ne!(service1_ptr, service2_ptr);
199    }
200
201    #[test]
202    fn test_im_service_v1_v2_api_access() {
203        let config = create_test_config();
204        let im_service = ImService::new(config);
205
206        // Verify that both v1 and v2 APIs are accessible
207        let v1_ptr = std::ptr::addr_of!(im_service.v1) as *const u8;
208        let v2_ptr = std::ptr::addr_of!(im_service.v2) as *const u8;
209
210        assert!(
211            !v1_ptr.is_null(),
212            "V1 service should be properly instantiated"
213        );
214        assert!(
215            !v2_ptr.is_null(),
216            "V2 service should be properly instantiated"
217        );
218        assert_ne!(
219            v1_ptr, v2_ptr,
220            "V1 and V2 services should be independent instances"
221        );
222    }
223
224    #[test]
225    fn test_im_service_v1_api_completeness() {
226        let config = create_test_config();
227        let im_service = ImService::new(config);
228
229        // Test V1 API structure exists and is accessible
230        let v1_ptr = std::ptr::addr_of!(im_service.v1) as *const u8;
231        assert!(!v1_ptr.is_null(), "V1 IM API should be instantiated");
232    }
233
234    #[test]
235    fn test_im_service_v2_api_completeness() {
236        let config = create_test_config();
237        let im_service = ImService::new(config);
238
239        // Test V2 API structure exists and is accessible
240        let v2_ptr = std::ptr::addr_of!(im_service.v2) as *const u8;
241        assert!(!v2_ptr.is_null(), "V2 IM API should be instantiated");
242    }
243
244    #[test]
245    fn test_im_service_concurrent_creation() {
246        let configs = vec![
247            Config::builder()
248                .app_id("im_concurrent_1")
249                .app_secret("secret_1")
250                .build(),
251            Config::builder()
252                .app_id("im_concurrent_2")
253                .app_secret("secret_2")
254                .build(),
255            Config::builder()
256                .app_id("im_concurrent_3")
257                .app_secret("secret_3")
258                .build(),
259        ];
260
261        let mut services = Vec::new();
262        for config in configs {
263            let service = ImService::new(config);
264            services.push(service);
265        }
266
267        // All services should be created successfully
268        assert_eq!(services.len(), 3);
269
270        // Verify all services are independent
271        for (i, service1) in services.iter().enumerate() {
272            for service2 in services.iter().skip(i + 1) {
273                let ptr1 = std::ptr::addr_of!(*service1) as *const u8;
274                let ptr2 = std::ptr::addr_of!(*service2) as *const u8;
275                assert_ne!(ptr1, ptr2, "Services should be independent instances");
276            }
277        }
278    }
279
280    #[test]
281    fn test_im_service_extreme_configurations() {
282        let extreme_configs = vec![
283            // Very short timeout
284            Config::builder()
285                .app_id("im_fast")
286                .app_secret("fast_secret")
287                .req_timeout(std::time::Duration::from_millis(100))
288                .build(),
289            // Very long timeout
290            Config::builder()
291                .app_id("im_slow")
292                .app_secret("slow_secret")
293                .req_timeout(std::time::Duration::from_secs(300))
294                .build(),
295            // Token cache disabled
296            Config::builder()
297                .app_id("im_no_cache")
298                .app_secret("no_cache_secret")
299                .enable_token_cache(false)
300                .build(),
301            // Custom base URL
302            Config::builder()
303                .app_id("im_custom_base")
304                .app_secret("custom_base_secret")
305                .base_url("https://custom.im.api.endpoint")
306                .build(),
307        ];
308
309        for config in extreme_configs {
310            let im_service = ImService::new(config);
311
312            // Each service should be created successfully regardless of extreme config
313            let service_ptr = std::ptr::addr_of!(im_service) as *const u8;
314            assert!(
315                !service_ptr.is_null(),
316                "Service should be created with extreme config"
317            );
318        }
319    }
320
321    #[test]
322    fn test_im_service_api_version_structure() {
323        let config = create_test_config();
324        let im_service = ImService::new(config);
325
326        // Verify the service contains exactly two API versions
327        let v1_offset = unsafe {
328            std::ptr::addr_of!(im_service.v1) as usize - std::ptr::addr_of!(im_service) as usize
329        };
330        let v2_offset = unsafe {
331            std::ptr::addr_of!(im_service.v2) as usize - std::ptr::addr_of!(im_service) as usize
332        };
333
334        // V1 and V2 should have different memory offsets
335        assert_ne!(
336            v1_offset, v2_offset,
337            "V1 and V2 should occupy different memory positions"
338        );
339
340        // Both offsets should be reasonable (within struct bounds)
341        assert!(v1_offset < 4096, "V1 offset should be reasonable");
342        assert!(v2_offset < 4096, "V2 offset should be reasonable");
343    }
344
345    #[test]
346    fn test_im_service_memory_consistency() {
347        let config = create_test_config();
348        let im_service = ImService::new(config);
349
350        // Test that the service maintains memory consistency across accesses
351        let service_ptr1 = std::ptr::addr_of!(im_service) as *const u8;
352        let service_ptr2 = std::ptr::addr_of!(im_service) as *const u8;
353
354        assert_eq!(
355            service_ptr1, service_ptr2,
356            "Service memory address should be consistent"
357        );
358
359        // Test API version consistency
360        let v1_ptr1 = std::ptr::addr_of!(im_service.v1) as *const u8;
361        let v1_ptr2 = std::ptr::addr_of!(im_service.v1) as *const u8;
362        let v2_ptr1 = std::ptr::addr_of!(im_service.v2) as *const u8;
363        let v2_ptr2 = std::ptr::addr_of!(im_service.v2) as *const u8;
364
365        assert_eq!(
366            v1_ptr1, v1_ptr2,
367            "V1 API memory address should be consistent"
368        );
369        assert_eq!(
370            v2_ptr1, v2_ptr2,
371            "V2 API memory address should be consistent"
372        );
373    }
374
375    #[test]
376    fn test_im_service_config_variations() {
377        let variations = vec![
378            (
379                "minimal",
380                Config::builder()
381                    .app_id("minimal")
382                    .app_secret("secret")
383                    .build(),
384            ),
385            (
386                "with_timeout",
387                Config::builder()
388                    .app_id("timeout")
389                    .app_secret("secret")
390                    .req_timeout(std::time::Duration::from_millis(30000))
391                    .build(),
392            ),
393            (
394                "with_base_url",
395                Config::builder()
396                    .app_id("base_url")
397                    .app_secret("secret")
398                    .base_url("https://test.api.com")
399                    .build(),
400            ),
401            (
402                "full_featured",
403                Config::builder()
404                    .app_id("full")
405                    .app_secret("secret")
406                    .req_timeout(std::time::Duration::from_millis(45000))
407                    .base_url("https://full.test.api.com")
408                    .enable_token_cache(true)
409                    .build(),
410            ),
411        ];
412
413        let mut services = Vec::new();
414        for (name, config) in variations {
415            let service = ImService::new(config);
416            services.push((name, service));
417        }
418
419        // All variations should create valid services
420        assert_eq!(services.len(), 4);
421
422        // Test that all services are independent
423        for (i, (_, service1)) in services.iter().enumerate() {
424            for (_, service2) in services.iter().skip(i + 1) {
425                let ptr1 = std::ptr::addr_of!(*service1) as *const u8;
426                let ptr2 = std::ptr::addr_of!(*service2) as *const u8;
427                assert_ne!(
428                    ptr1, ptr2,
429                    "Services with different configs should be independent"
430                );
431            }
432        }
433    }
434}