tuiserial_serial/
lib.rs

1//! Serial port communication library for tuiserial
2//!
3//! This crate provides serial port operations including port enumeration,
4//! connection management, and data transmission.
5
6use serialport::SerialPort;
7use std::time::Duration;
8use tuiserial_core::{FlowControl, Parity, SerialConfig};
9
10// Re-exports
11pub use serialport;
12pub use tokio;
13
14/// List all available serial ports on the system
15pub fn list_ports() -> Vec<String> {
16    match serialport::available_ports() {
17        Ok(ports) => ports.iter().map(|p| p.port_name.clone()).collect(),
18        Err(_) => Vec::new(),
19    }
20}
21
22/// Open a serial port with the given configuration
23pub fn open_port(config: &SerialConfig) -> Result<Box<dyn SerialPort>, String> {
24    serialport::new(&config.port, config.baud_rate)
25        .timeout(Duration::from_millis(10))
26        .data_bits(match config.data_bits {
27            5 => serialport::DataBits::Five,
28            6 => serialport::DataBits::Six,
29            7 => serialport::DataBits::Seven,
30            _ => serialport::DataBits::Eight,
31        })
32        .parity(match config.parity {
33            Parity::Even => serialport::Parity::Even,
34            Parity::Odd => serialport::Parity::Odd,
35            Parity::None => serialport::Parity::None,
36        })
37        .stop_bits(match config.stop_bits {
38            1 => serialport::StopBits::One,
39            2 => serialport::StopBits::Two,
40            _ => serialport::StopBits::One,
41        })
42        .flow_control(match config.flow_control {
43            FlowControl::Hardware => serialport::FlowControl::Hardware,
44            FlowControl::Software => serialport::FlowControl::Software,
45            FlowControl::None => serialport::FlowControl::None,
46        })
47        .open_native()
48        .map_err(|e| format!("Failed to open port: {}", e))
49        .map(|p| Box::new(p) as Box<dyn SerialPort>)
50}
51
52/// Read data from the serial port
53pub fn read_data(port: &mut dyn SerialPort) -> Result<Vec<u8>, String> {
54    let mut buf = vec![0u8; 256];
55    match port.read(buf.as_mut_slice()) {
56        Ok(n) if n > 0 => {
57            buf.truncate(n);
58            Ok(buf)
59        }
60        Ok(_) => Ok(Vec::new()),
61        Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => Ok(Vec::new()),
62        Err(e) => Err(format!("Read error: {}", e)),
63    }
64}
65
66/// Write data to the serial port
67pub fn write_data(port: &mut dyn SerialPort, data: &[u8]) -> Result<usize, String> {
68    port.write_all(data)
69        .map(|_| data.len())
70        .map_err(|e| format!("Write error: {}", e))
71}
72
73/// Convert hex string to bytes
74///
75/// # Example
76/// ```
77/// use tuiserial_serial::hex_to_bytes;
78/// let bytes = hex_to_bytes("48656C6C6F").unwrap();
79/// assert_eq!(bytes, vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]);
80/// ```
81pub fn hex_to_bytes(hex_str: &str) -> Result<Vec<u8>, String> {
82    let hex_str = hex_str.trim().replace(" ", "");
83    if hex_str.len() % 2 != 0 {
84        return Err("Hex string must have even length".to_string());
85    }
86
87    hex_str
88        .chars()
89        .collect::<Vec<_>>()
90        .chunks(2)
91        .map(|chunk| {
92            u8::from_str_radix(&chunk.iter().collect::<String>(), 16)
93                .map_err(|_| "Invalid hex character".to_string())
94        })
95        .collect()
96}
97
98/// Convert bytes to hex string representation
99///
100/// # Example
101/// ```
102/// use tuiserial_serial::bytes_to_hex;
103/// let hex = bytes_to_hex(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]);
104/// assert_eq!(hex, "48 65 6C 6C 6F");
105/// ```
106pub fn bytes_to_hex(bytes: &[u8]) -> String {
107    bytes
108        .iter()
109        .map(|b| format!("{:02X}", b))
110        .collect::<Vec<_>>()
111        .join(" ")
112}
113
114/// Convert bytes to string, escaping non-printable characters
115///
116/// # Example
117/// ```
118/// use tuiserial_serial::bytes_to_string;
119/// let s = bytes_to_string(&[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A]);
120/// assert_eq!(s, "Hello\\x0A");
121/// ```
122pub fn bytes_to_string(bytes: &[u8]) -> String {
123    bytes
124        .iter()
125        .map(|&b| {
126            if b >= 32 && b < 127 {
127                (b as char).to_string()
128            } else {
129                format!("\\x{:02X}", b)
130            }
131        })
132        .collect()
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_hex_to_bytes() {
141        assert_eq!(
142            hex_to_bytes("48656C6C6F").unwrap(),
143            vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]
144        );
145        assert_eq!(
146            hex_to_bytes("48 65 6C 6C 6F").unwrap(),
147            vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]
148        );
149        assert!(hex_to_bytes("4865F").is_err()); // Odd length
150        assert!(hex_to_bytes("48XY").is_err()); // Invalid hex
151    }
152
153    #[test]
154    fn test_bytes_to_hex() {
155        assert_eq!(
156            bytes_to_hex(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]),
157            "48 65 6C 6C 6F"
158        );
159        assert_eq!(bytes_to_hex(&[]), "");
160    }
161
162    #[test]
163    fn test_bytes_to_string() {
164        assert_eq!(bytes_to_string(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]), "Hello");
165        assert_eq!(
166            bytes_to_string(&[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A]),
167            "Hello\\x0A"
168        );
169        assert_eq!(bytes_to_string(&[0x00, 0x1F, 0x7F]), "\\x00\\x1F\\x7F");
170    }
171}