actr_runtime/transport/
wire_builder.rs

1//! WireBuilder - Wire layer component builder
2//!
3//! Provides default Wire component builder implementation, supporting:
4//! - WebRTC P2P connections (through WebRtcCoordinator)
5//! - WebSocket C/S connections
6
7use super::Dest; // Re-exported from actr-framework
8use super::error::NetworkResult;
9use super::manager::WireBuilder;
10use super::wire_handle::WireHandle;
11use crate::wire::webrtc::WebRtcCoordinator;
12use crate::wire::websocket::WebSocketConnection;
13use async_trait::async_trait;
14use std::sync::Arc;
15
16/// Default Wire builder configuration
17pub struct DefaultWireBuilderConfig {
18    /// WebSocket server URL template (can contain {actor_id} placeholder for dynamic substitution)
19    pub websocket_url_template: Option<String>,
20
21    /// Enable WebRTC
22    pub enable_webrtc: bool,
23
24    /// Enable WebSocket
25    pub enable_websocket: bool,
26}
27
28impl Default for DefaultWireBuilderConfig {
29    fn default() -> Self {
30        Self {
31            websocket_url_template: None,
32            enable_webrtc: true,
33            enable_websocket: false, // WebSocket disabled by default (requires URL configuration)
34        }
35    }
36}
37
38/// default Wire construct build device
39///
40/// based onconfigurationCreate WebRTC and/or WebSocket Wire group file 。
41/// Supportsaturatedand format Connect( same temporal attempt try multiple typeConnectType)。
42pub struct DefaultWireBuilder {
43    /// WebRTC coordinator(optional)
44    webrtc_coordinator: Option<Arc<WebRtcCoordinator>>,
45
46    /// WebSocket URL vague template
47    websocket_url_template: Option<String>,
48
49    /// configuration
50    config: DefaultWireBuilderConfig,
51}
52
53impl DefaultWireBuilder {
54    /// Create new Wire construct build device
55    ///
56    /// # Arguments
57    /// - `webrtc_coordinator`: WebRTC coordinator(If start usage WebRTC)
58    /// - `config`: construct build device configuration
59    pub fn new(
60        webrtc_coordinator: Option<Arc<WebRtcCoordinator>>,
61        config: DefaultWireBuilderConfig,
62    ) -> Self {
63        Self {
64            webrtc_coordinator,
65            websocket_url_template: config.websocket_url_template.clone(),
66            config,
67        }
68    }
69
70    /// Build WebSocket URL from template
71    fn build_websocket_url(&self, dest: &Dest) -> Option<String> {
72        let template = self.websocket_url_template.as_ref()?;
73
74        match dest {
75            Dest::Actor(actor_id) => {
76                // Replace {actor_id} placeholder with serial_number
77                let url = template.replace("{actor_id}", &actor_id.serial_number.to_string());
78                Some(url)
79            }
80            Dest::Shell | Dest::Local => {
81                // Local/Shell calls don't need network connections (should be short-circuited at upper layer)
82                // Return None for type completeness
83                None
84            }
85        }
86    }
87}
88
89#[async_trait]
90impl WireBuilder for DefaultWireBuilder {
91    async fn create_connections(&self, dest: &Dest) -> NetworkResult<Vec<WireHandle>> {
92        let mut connections = Vec::new();
93
94        // 1. attempt try Create WebSocket Connect
95        if self.config.enable_websocket {
96            if let Some(url) = self.build_websocket_url(dest) {
97                tracing::debug!("🏭 [Factory] Create WebSocket Connect: {}", url);
98                let ws_conn = WebSocketConnection::new(url);
99                connections.push(WireHandle::WebSocket(ws_conn));
100            } else {
101                tracing::warn!(
102                    "⚠️ [Factory] WebSocket enabled but no method construct build URL: {:?}",
103                    dest
104                );
105            }
106        }
107
108        // 2. attempt try Create WebRTC Connect
109        if self.config.enable_webrtc {
110            if let Some(coordinator) = &self.webrtc_coordinator {
111                // WebRTC merely Support Actor Type
112                if dest.is_actor() {
113                    tracing::debug!("🏭 [Factory] Create WebRTC Connectto: {:?}", dest);
114                    match coordinator.create_connection(dest).await {
115                        Ok(webrtc_conn) => {
116                            connections.push(WireHandle::WebRTC(webrtc_conn));
117                        }
118                        Err(e) => {
119                            tracing::warn!(
120                                "❌ [Factory] WebRTC ConnectCreatefailure: {:?}: {}",
121                                dest,
122                                e
123                            );
124                            // not ReturnsError,allowusingotherConnectType
125                        }
126                    }
127                } else {
128                    tracing::debug!(
129                        "ℹ️ [Factory] WebRTC not Support Shell item mark ,skip through "
130                    );
131                }
132            } else {
133                tracing::warn!("⚠️ [Factory] WebRTC enabled but not Provide WebRtcCoordinator");
134            }
135        }
136
137        tracing::info!(
138            "✨ [Factory] as {:?} Create done {} Connect",
139            dest,
140            connections.len()
141        );
142
143        Ok(connections)
144    }
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use actr_protocol::ActrId;
151
152    #[test]
153    fn test_build_websocket_url() {
154        let config = DefaultWireBuilderConfig {
155            websocket_url_template: Some("ws://server:8080/actor/{actor_id}".to_string()),
156            enable_websocket: true,
157            enable_webrtc: false,
158        };
159
160        let factory = DefaultWireBuilder::new(None, config);
161
162        let mut actor_id = ActrId::default();
163        actor_id.serial_number = 12345;
164        let dest = Dest::Actor(actor_id);
165
166        let url = factory.build_websocket_url(&dest);
167        assert_eq!(url, Some("ws://server:8080/actor/12345".to_string()));
168    }
169
170    #[tokio::test]
171    async fn test_create_websocket_connection() {
172        use actr_protocol::ActrId;
173
174        let config = DefaultWireBuilderConfig {
175            websocket_url_template: Some("ws://localhost:8080".to_string()),
176            enable_websocket: true,
177            enable_webrtc: false,
178        };
179
180        let factory = DefaultWireBuilder::new(None, config);
181        // Use Actor dest instead of Shell (Shell doesn't create network connections)
182        let actor_id = ActrId::default();
183        let dest = Dest::actor(actor_id);
184
185        let connections = factory.create_connections(&dest).await.unwrap();
186        assert_eq!(connections.len(), 1);
187
188        if let WireHandle::WebSocket(_ws_conn) = &connections[0] {
189            // WebSocket ConnectCreatesuccess
190        } else {
191            panic!("Expected WebSocket connection");
192        }
193    }
194}