connect_io/lib.rs
1/*
2 * @Author : Easion_Wang Easion.YX@outlook.com
3 * @Date : 2026-05-29 18:31:03
4 * @FilePath : /connect-io/src/lib.rs
5 * @LastEditors : Easion_Wang Easion.YX@outlook.com
6 * @LastEditTime : 2026-05-30 15:12:39
7 * @Description :
8 */
9mod error;
10pub use error::TransportError;
11
12/// 连接状态枚举
13///
14/// 描述传输通道的当前连接状态,用于区分已连接、断开、连接中、未知等状态。
15/// 该枚举实现了 `Debug`、`Clone`、`PartialEq`、`Eq` trait,可安全跨线程传递和比较。
16///
17/// # Variants
18/// - `Connected`: 已建立稳定连接,可进行数据收发
19/// - `Disconnected`: 已断开连接,需要重新建立连接才能通信
20/// - `Connecting`: 正在尝试建立连接中(握手阶段)
21/// - `Unknown`: 状态未知,通常作为默认初始状态
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum ConnectionState {
24 /// 已建立连接
25 ///
26 /// 表示传输通道已成功建立,可以进行正常的数据读写操作。
27 /// 对于 TCP 意味着三次握手完成;对于串口意味着端口已打开;
28 /// 对于 UDP 意味着 socket 已绑定并可选地 connect 到对端。
29 Connected,
30
31 /// 已断开连接
32 ///
33 /// 表示传输通道已关闭或连接丢失。
34 /// 可能由主动关闭、网络中断、超时等原因导致。
35 Disconnected,
36
37 /// 正在建立连接中
38 ///
39 /// 表示正在进行连接建立过程(如 TCP 三次握手),
40 /// 尚未完成,此时不应进行数据传输操作。
41 Connecting,
42
43 /// 状态未知(默认值)
44 ///
45 /// 初始默认状态或无法确定当前连接状态时使用。
46 /// 各传输实现应尽可能覆盖此默认值以提供准确状态。
47 Unknown,
48}
49
50#[cfg(feature = "tcp")]
51pub mod tcp;
52#[cfg(feature = "tcp")]
53pub mod tcp_server;
54#[cfg(feature = "udp")]
55pub mod udp;
56#[cfg(feature = "serial")]
57pub mod serial;
58
59#[cfg(feature = "async")]
60pub mod async_impl;
61
62#[cfg(feature = "tauri")]
63pub mod tauri_plugin;
64
65#[cfg(feature = "tauri")]
66pub use tauri_plugin::TransportState;
67
68use std::io::{Read, Write};
69use std::net::SocketAddr;
70use std::time::Duration;
71
72/// 传输配置枚举
73///
74/// 定义所有支持的传输协议及其配置参数。
75/// 该枚举作为 `Transport::connect()` 和 `create_transport()` 工厂函数的输入,
76/// 用于指定要创建的传输类型和连接参数。
77///
78/// # Feature Gates
79/// - `TcpClient` / `TcpServer`: 需要 `tcp` feature
80/// - `Udp`: 需要 `udp` feature
81/// - `Serial`: 需要 `serial` feature
82///
83/// # Example
84/// ```ignore
85/// use std::net::SocketAddr;
86/// use connect_io::TransportConfig;
87///
88/// // TCP 客户端配置
89/// let addr: SocketAddr = "192.168.1.100:8080".parse().unwrap();
90/// let config = TransportConfig::TcpClient { addr };
91///
92/// // UDP 配置(带默认对端)
93/// let bind_addr: SocketAddr = "0.0.0.0:9000".parse().unwrap();
94/// let peer_addr: SocketAddr = "192.168.1.100:9000".parse().unwrap();
95/// let udp_config = TransportConfig::Udp {
96/// bind_addr,
97/// peer_addr: Some(peer_addr),
98/// };
99/// ```
100#[derive(Debug, Clone)]
101pub enum TransportConfig {
102 /// TCP 客户端连接配置
103 ///
104 /// 用于创建主动发起连接的 TCP 客户端。
105 /// 连接建立后可进行双向字节流通信。
106 ///
107 /// # Fields
108 /// * `addr` - 目标服务器的 Socket 地址(IP:Port)
109 TcpClient { addr: SocketAddr },
110
111 /// TCP 服务端监听配置
112 ///
113 /// 用于创建 TCP 服务端监听器,等待客户端连接。
114 /// 注意:此变体用于 `TcpTransport::connect()` 中的服务端模式,
115 /// 如需管理多个连接建议使用 `TcpServerManager`。
116 ///
117 /// # Fields
118 /// * `bind_addr` - 本地绑定的监听地址
119 TcpServer { bind_addr: SocketAddr },
120
121 /// UDP 传输配置
122 ///
123 /// 用于创建 UDP socket,支持有连接和无连接两种模式:
124 /// - 指定 `peer_addr` 时为已连接模式,可直接使用 read/write
125 /// - 不指定 `peer_addr` 时为无连接模式,需使用 send_to/recv_from
126 ///
127 /// # Fields
128 /// * `bind_addr` - 本地绑定的地址
129 /// * `peer_addr` - 可选的对端地址,None 表示无连接模式
130 Udp {
131 bind_addr: SocketAddr,
132 peer_addr: Option<SocketAddr>,
133 },
134
135 /// 串口传输配置
136 ///
137 /// 用于创建串口(RS-232/RS-485)连接。
138 /// 串口是点对点通信,打开端口即视为已连接。
139 ///
140 /// # Feature
141 /// 需要启用 `serial` feature 才可使用此变体。
142 ///
143 /// # Fields
144 /// * `port` - 串口设备路径(如 "/dev/ttyUSB0" 或 "COM3")
145 /// * `baud_rate` - 波特率(如 9600、115200)
146 /// * `data_bits` - 数据位(通常为 8)
147 /// * `stop_bits` - 停止位
148 /// * `parity` - 校验位
149 /// * `flow_control` - 流控制模式
150 #[cfg(feature = "serial")]
151 Serial {
152 port: String,
153 baud_rate: u32,
154 data_bits: serialport::DataBits,
155 stop_bits: serialport::StopBits,
156 parity: serialport::Parity,
157 flow_control: serialport::FlowControl,
158 },
159}
160
161/// 统一传输 Trait(同步版本)
162///
163/// 定义同步传输通道的通用接口,支持 TCP、UDP、串口等多种传输协议。
164/// 实现此 trait 的类型必须同时实现 `std::io::Read` 和 `std::io::Write`,
165/// 以提供标准的字节流读写能力。
166///
167/// # Lifecycle
168/// 1. 调用 `connect()` 建立连接
169/// 2. 使用 `read()`/`write()` 进行数据传输
170/// 3. 调用 `close()` 关闭连接
171///
172/// # Thread Safety
173/// 实现 `Send` 即可在多线程间移动所有权(`&mut self` 方法需要外部同步)。
174///
175/// # Example
176/// ```ignore
177/// use connect_io::{Transport, TransportConfig, TcpTransport};
178/// use std::net::SocketAddr;
179///
180/// let addr: SocketAddr = "127.0.0.1:8080".parse().unwrap();
181/// let config = TransportConfig::TcpClient { addr };
182/// let mut transport = TcpTransport::connect(config)?;
183///
184/// // 写入数据
185/// transport.write_all(b"Hello")?;
186/// transport.flush()?;
187///
188/// // 读取响应
189/// let mut buf = [0u8; 1024];
190/// let n = transport.read(&mut buf)?;
191///
192/// transport.close()?;
193/// # Ok::<(), connect_io::TransportError>(())
194/// ```
195pub trait Transport: Read + Write {
196 /// 根据配置建立传输连接
197 ///
198 /// 创建并初始化传输通道。对于 TCP 客户端会发起连接到目标服务器;
199 /// 对于 TCP 服务端会绑定端口并等待第一个客户端连接;
200 /// 对于 UDP 会绑定本地 socket;对于串口会打开指定端口。
201 ///
202 /// # Arguments
203 /// * `config` - 传输配置,必须与具体实现类型匹配
204 ///
205 /// # Returns
206 /// 成功返回已连接的传输实例,失败返回对应错误
207 ///
208 /// # Errors
209 /// - `Config` - 配置类型不匹配(如用 TCP 配置创建 UDP 传输)
210 /// - `ConnectionFailed` - 连接建立失败(网络不可达、端口被占用等)
211 /// - `Io` - 底层 I/O 错误(权限不足、设备不存在等)
212 fn connect(config: TransportConfig) -> Result<Self, TransportError>
213 where
214 Self: Sized;
215
216 /// 关闭传输连接
217 ///
218 /// 释放底层资源并关闭传输通道。
219 /// 对于 TCP 会发送 FIN 包执行优雅关闭;
220 /// 对于 UDP 和串口仅释放资源。
221 ///
222 /// # Returns
223 /// 成功返回 `Ok(())`,失败返回错误
224 ///
225 /// # Note
226 /// 调用后对象不应再用于数据传输,但可以重新调用 `connect()` 建立新连接(如果实现支持)。
227 fn close(&mut self) -> Result<(), TransportError>;
228
229 /// 检查当前是否处于已连接状态
230 ///
231 /// 快速检查传输通道是否可用。
232 /// 不同协议的判断方式不同:
233 /// - TCP: 通过 peek 操作检测 socket 是否存活
234 /// - UDP: 检查是否已调用 connect()
235 /// - 串口: 始终返回 true(打开即视为连接)
236 ///
237 /// # Returns
238 /// - `true` - 已连接,可进行数据传输
239 /// - `false` - 未连接或连接已断开
240 fn is_connected(&self) -> bool;
241
242 /// 设置读写超时时间
243 ///
244 /// 配置后续所有 read/write 操作的超时时间。
245 /// 超时后操作会返回 `TimedOut` 错误。
246 ///
247 /// # Arguments
248 /// * `timeout` - 超时时间,None 表示无限等待,Some(duration) 表示指定超时
249 ///
250 /// # Returns
251 /// 成功返回 `Ok(())`,失败返回错误
252 ///
253 /// # Example
254 /// ```ignore
255 /// use std::time::Duration;
256 ///
257 /// // 设置 10 秒超时
258 /// transport.set_timeout(Some(Duration::from_secs(10)))?;
259 ///
260 /// // 取消超时(阻塞等待)
261 /// transport.set_timeout(None)?;
262 /// ```
263 fn set_timeout(&mut self, timeout: Option<Duration>) -> Result<(), TransportError>;
264
265 /// 获取本地绑定的地址
266 ///
267 /// 返回传输层本地绑定的 socket 地址。
268 /// 对于服务端模式返回监听地址;对于客户端返回操作系统分配的本地地址。
269 ///
270 /// # Returns
271 /// - `Some(SocketAddr)` - 本地地址获取成功
272 /// - `None` - 不支持或获取失败(如某些虚拟传输)
273 fn local_addr(&self) -> Option<SocketAddr> {
274 None
275 }
276
277 /// 获取远程对端的地址
278 ///
279 /// 返回传输层对端的 socket 地址。
280 /// 仅在已连接模式下有意义(TCP 已连接、UDP 已 connect)。
281 ///
282 /// # Returns
283 /// - `Some(SocketAddr)` - 对端地址获取成功
284 /// - `None` - 未连接或不支持
285 fn peer_addr(&self) -> Option<SocketAddr> {
286 None
287 }
288
289 /// 获取当前连接状态
290 ///
291 /// 返回详细的连接状态信息,比 `is_connected()` 提供更多语义信息。
292 /// 默认实现返回 `Unknown`,各传输类型应覆盖以提供准确状态。
293 ///
294 /// # Returns
295 /// 当前连接状态枚举值:
296 /// - `Connected`: 已连接且可用
297 /// - `Disconnected`: 已断开
298 /// - `Connecting`: 正在连接中
299 /// - `Unknown`: 状态未知
300 fn connection_state(&self) -> ConnectionState {
301 ConnectionState::Unknown
302 }
303}
304
305/// 传输工厂函数(同步版本)
306///
307/// 根据 `TransportConfig` 配置创建对应的传输实例,返回 trait 对象。
308/// 这是创建传输通道的推荐入口点,无需关心具体实现类型。
309///
310/// 该函数会根据配置自动选择对应的传输实现:
311/// - `TcpClient` / `TcpServer` → `TcpTransport`
312/// - `Udp` → `UdpTransport`
313/// - `Serial` → `SerialTransport`
314///
315/// # Arguments
316/// * `config` - 传输配置,决定创建的传输类型和参数
317///
318/// # Returns
319/// 成功返回装箱后的 `dyn Transport` trait 对象,
320/// 失败返回对应的错误(配置错误、连接失败、I/O 错误等)
321///
322/// # Errors
323/// - `Config("TCP feature not enabled")` - 尝试使用 TCP 但未启用 tcp feature
324/// - `Config("UDP feature not enabled")` - 尝试使用 UDP 但未启用 udp feature
325/// - `Config("Serial feature not enabled")` - 尝试使用串口但未启用 serial feature
326/// - `ConnectionFailed(...)` - 连接建立失败
327/// - `Io(...)` - 底层 I/O 错误
328///
329/// # Example
330/// ```ignore
331/// use connect_io::{create_transport, Transport, TransportConfig};
332/// use std::net::SocketAddr;
333///
334/// // 创建 TCP 客户端
335/// let addr: SocketAddr = "192.168.1.100:8080".parse().unwrap();
336/// let config = TransportConfig::TcpClient { addr };
337/// let mut transport = create_transport(config)?;
338///
339/// // 使用 transport 进行通信...
340/// transport.write_all(b"Hello World")?;
341/// transport.close()?;
342/// # Ok::<(), connect_io::TransportError>(())
343/// ```
344///
345/// # Feature Gates
346/// 各传输类型需要对应 feature 启用:
347/// - TCP: `tcp`
348/// - UDP: `udp`
349/// - Serial: `serial`
350pub fn create_transport(config: TransportConfig) -> Result<Box<dyn Transport>, TransportError> {
351 match &config {
352 TransportConfig::TcpClient { .. } | TransportConfig::TcpServer { .. } => {
353 #[cfg(feature = "tcp")]
354 {
355 Ok(Box::new(tcp::TcpTransport::connect(config)?))
356 }
357 #[cfg(not(feature = "tcp"))]
358 {
359 Err(TransportError::Config("TCP feature not enabled".into()))
360 }
361 }
362 TransportConfig::Udp { .. } => {
363 #[cfg(feature = "udp")]
364 {
365 Ok(Box::new(udp::UdpTransport::connect(config)?))
366 }
367 #[cfg(not(feature = "udp"))]
368 {
369 Err(TransportError::Config("UDP feature not enabled".into()))
370 }
371 }
372 #[cfg(feature = "serial")]
373 TransportConfig::Serial { .. } => {
374 #[cfg(feature = "serial")]
375 {
376 Ok(Box::new(serial::SerialTransport::connect(config)?))
377 }
378 #[cfg(not(feature = "serial"))]
379 {
380 Err(TransportError::Config("Serial feature not enabled".into()))
381 }
382 }
383 #[cfg(not(feature = "serial"))]
384 _ => {
385 // 这个分支理论上不会到达,因为我们已经在上面用 cfg 过滤了
386 Err(TransportError::Config("Unknown config".into()))
387 }
388 }
389}
390
391#[cfg(feature = "async")]
392pub use async_impl::{AsyncTransportConfig, AsyncTcpTransport, AsyncUdpTransport, AsyncSerialTransport, AsyncTcpServerManager, create_async_transport};
393
394#[cfg(test)]
395#[allow(non_snake_case)]
396mod tests {
397 use super::*;
398 use std::net::SocketAddr;
399
400 // ============================================================
401 // TransportConfig 构造验证
402 // ============================================================
403
404 #[test]
405 fn FunEvent_config_tcp_client_constructs() {
406 let addr: SocketAddr = "127.0.0.1:8080".parse().expect("parse addr");
407 let config = TransportConfig::TcpClient { addr };
408
409 match config {
410 TransportConfig::TcpClient { addr: a } => {
411 assert_eq!(a, "127.0.0.1:8080".parse::<SocketAddr>().unwrap());
412 }
413 _ => panic!("expected TcpClient variant"),
414 }
415 }
416
417 #[test]
418 fn FunEvent_config_tcp_server_constructs() {
419 let addr: SocketAddr = "0.0.0.0:9000".parse().expect("parse addr");
420 let config = TransportConfig::TcpServer { bind_addr: addr };
421
422 match config {
423 TransportConfig::TcpServer { bind_addr } => {
424 assert_eq!(bind_addr.port(), 9000);
425 }
426 _ => panic!("expected TcpServer variant"),
427 }
428 }
429
430 #[test]
431 fn FunEvent_config_udp_with_peer_constructs() {
432 let bind: SocketAddr = "0.0.0.0:0".parse().unwrap();
433 let peer: SocketAddr = "192.168.1.1:5000".parse().unwrap();
434 let config = TransportConfig::Udp {
435 bind_addr: bind,
436 peer_addr: Some(peer),
437 };
438
439 match config {
440 TransportConfig::Udp {
441 bind_addr,
442 peer_addr,
443 } => {
444 assert_eq!(bind_addr.port(), 0);
445 assert_eq!(peer_addr, Some(peer));
446 }
447 _ => panic!("expected Udp variant"),
448 }
449 }
450
451 #[test]
452 fn FunEvent_config_udp_without_peer_constructs() {
453 let bind: SocketAddr = "0.0.0.0:9999".parse().unwrap();
454 let config = TransportConfig::Udp {
455 bind_addr: bind,
456 peer_addr: None,
457 };
458
459 match config {
460 TransportConfig::Udp { peer_addr, .. } => {
461 assert!(peer_addr.is_none(), "peer_addr should be None");
462 }
463 _ => panic!("expected Udp variant"),
464 }
465 }
466
467 // ============================================================
468 // 工厂函数 feature gate 测试
469 // ============================================================
470
471 #[cfg(not(feature = "tcp"))]
472 #[test]
473 fn FunEvent_factory_tcp_disabled_returns_error() {
474 let config = TransportConfig::TcpClient {
475 addr: "127.0.0.1:8080".parse().unwrap(),
476 };
477 let result = create_transport(config);
478 assert!(result.is_err(), "factory should return error when TCP disabled");
479 match result.unwrap_err() {
480 TransportError::Config(msg) => {
481 assert!(
482 msg.contains("TCP feature not enabled"),
483 "error message should mention feature disabled, got: {}",
484 msg
485 );
486 }
487 other => panic!("expected Config error, got: {:?}", other),
488 }
489 }
490
491 #[cfg(not(feature = "udp"))]
492 #[test]
493 fn FunEvent_factory_udp_disabled_returns_error() {
494 let config = TransportConfig::Udp {
495 bind_addr: "0.0.0.0:0".parse().unwrap(),
496 peer_addr: None,
497 };
498 let result = create_transport(config);
499 assert!(result.is_err(), "factory should return error when UDP disabled");
500 match result.unwrap_err() {
501 TransportError::Config(msg) => {
502 assert!(
503 msg.contains("UDP feature not enabled"),
504 "error message should mention feature disabled"
505 );
506 }
507 other => panic!("expected Config error, got: {:?}", other),
508 }
509 }
510
511 // ============================================================
512 // TransportError 变体覆盖
513 // ============================================================
514
515 #[test]
516 fn FunEvent_error_config_variant() {
517 let err = TransportError::Config("test config error".into());
518 let msg = format!("{}", err);
519 assert!(
520 msg.contains("Invalid configuration"),
521 "Config error Display should contain 'Invalid configuration', got: {}",
522 msg
523 );
524 assert!(
525 msg.contains("test config error"),
526 "Config error Display should contain original message"
527 );
528 }
529
530 #[test]
531 fn FunEvent_error_connection_failed_variant() {
532 let err = TransportError::ConnectionFailed("peer refused".into());
533 let msg = format!("{}", err);
534 assert!(
535 msg.contains("Connection failed"),
536 "ConnectionFailed Display should contain prefix"
537 );
538 assert!(
539 msg.contains("peer refused"),
540 "ConnectionFailed Display should contain detail"
541 );
542 }
543
544 #[test]
545 fn FunEvent_error_io_from_conversion() {
546 let io_err = std::io::Error::new(std::io::ErrorKind::BrokenPipe, "pipe broken");
547 let transport_err = TransportError::from(io_err);
548 let msg = format!("{}", transport_err);
549 assert!(
550 msg.contains("IO error"),
551 "Io variant Display should contain 'IO error'"
552 );
553 assert!(
554 msg.contains("pipe broken"),
555 "Io variant Display should contain original io message"
556 );
557 }
558
559 // ============================================================
560 // Transport trait 默认方法验证
561 // ============================================================
562
563 #[test]
564 fn FunEvent_transport_default_methods_exist() {
565 // 验证 trait 对象可以调用默认实现(编译期检查)
566 // 这里仅验证 trait bound 可以满足,不实际构造对象
567 fn _assert_trait_bounds<T: Transport>() {}
568
569 // 编译通过即说明 trait 方法存在
570 _assert_trait_bounds::<tcp::TcpTransport>();
571 #[cfg(feature = "udp")]
572 _assert_trait_bounds::<udp::UdpTransport>();
573 }
574}
575
576#[cfg(feature = "tcp")]
577pub use tcp_server::TcpServerManager;