Skip to main content

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;