Skip to main content

dingtalk_stream/client/stream_/
mod.rs

1use crate::client::AccessTokenCache;
2use crate::handlers::{
3    CallbackHandler, DefaultLifecycleListener, EventHandler, LifecycleListener, SystemHandler,
4};
5use crate::{ClientConfig, Credential};
6use std::collections::HashMap;
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::sync::Arc;
9use tokio::sync::RwLock;
10
11mod access_token;
12mod download_resources;
13mod upload_resources;
14pub use upload_resources::*;
15mod handle_message;
16mod lifecycle;
17mod send_message;
18use crate::frames::down_message::MessageTopic;
19pub use download_resources::DingtalkResource;
20
21/// DingTalk Stream Client
22pub struct DingTalkStream {
23    /// Credential for authentication
24    pub(super) credential: Credential,
25    /// Client configuration
26    config: ClientConfig,
27    /// Event handler
28    event_handler: Option<Arc<dyn EventHandler>>,
29    /// Callback handlers mapped by topic
30    callback_handlers: HashMap<MessageTopic, Arc<dyn CallbackHandler>>,
31    /// System handler
32    system_handler: Option<Arc<dyn SystemHandler>>,
33    /// Whether connected
34    connected: AtomicBool,
35    /// Whether registered
36    registered: AtomicBool,
37    /// Access token cache
38    access_token: Arc<RwLock<Option<AccessTokenCache>>>,
39    http_client: reqwest::Client,
40    lifecycle_listener: Arc<dyn LifecycleListener>,
41}
42
43impl DingTalkStream {
44    /// Create a new DingTalk Stream client
45    pub fn new(credential: Credential) -> Self {
46        Self::with_config(credential, ClientConfig::default())
47    }
48
49    /// Create with custom configuration
50    pub fn with_config(credential: Credential, config: ClientConfig) -> Self {
51        Self {
52            credential,
53            config,
54            event_handler: None,
55            callback_handlers: HashMap::default(),
56            system_handler: None,
57            connected: AtomicBool::new(false),
58            registered: AtomicBool::new(false),
59            access_token: Default::default(),
60            http_client: reqwest::Client::default(),
61            lifecycle_listener: Arc::new(DefaultLifecycleListener::default()),
62        }
63    }
64}
65
66impl DingTalkStream {
67    /// Register an event handler
68    pub async fn register_event_handler<H: EventHandler + 'static>(
69        mut self,
70        handler: Arc<H>,
71    ) -> Self {
72        self.event_handler.replace(handler);
73        self
74    }
75
76    /// Register a callback handler for a specific topic
77    pub async fn register_callback_handler<H: CallbackHandler + 'static>(
78        mut self,
79        handler: Arc<H>,
80    ) -> Self {
81        let topic = handler.topic().clone();
82        self.callback_handlers.insert(topic, handler);
83        self
84    }
85
86    /// Register a system handler
87    pub async fn register_system_handler<H: SystemHandler + 'static>(
88        mut self,
89        handler: Arc<H>,
90    ) -> Self {
91        self.system_handler.replace(handler);
92        self
93    }
94
95    pub async fn register_lifecycle_listener<L: LifecycleListener + 'static>(
96        mut self,
97        listener: Arc<L>,
98    ) -> Self {
99        self.lifecycle_listener = listener;
100        self
101    }
102}
103
104impl DingTalkStream {
105    /// Check if connected
106    pub fn is_connected(&self) -> bool {
107        self.connected.load(Ordering::Relaxed)
108    }
109
110    /// Check if registered
111    pub fn is_registered(&self) -> bool {
112        self.registered.load(Ordering::Relaxed)
113    }
114
115    /// Get the credential
116    pub fn credential(&self) -> &Credential {
117        &self.credential
118    }
119
120    /// Get configuration
121    pub fn config(&self) -> &ClientConfig {
122        &self.config
123    }
124}