├── .gitignore ├── img ├── cli.png └── htop.gif ├── README.md ├── Cargo.toml ├── LICENSE ├── src ├── uart_readout.rs ├── main.rs ├── args_parse.rs ├── utf8_buffer.rs └── key_listener.rs └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .vscode/ -------------------------------------------------------------------------------- /img/cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenxuuu/serialport_monitor/HEAD/img/cli.png -------------------------------------------------------------------------------- /img/htop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenxuuu/serialport_monitor/HEAD/img/htop.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serialport_monitor 2 | 3 | 用自己喜欢的终端打印串口日志。pure serialport data printer, for your favorite terminal. 4 | 5 | 可以代替MobaXterm、SecureCRT、PuTTy等串口连接SSH或查看日志的软件。 6 | 7 | ![cli](img/cli.png) 8 | 9 | ![htop](img/htop.gif) 10 | 11 | 我是搭配window terminal用,挺好 12 | 13 | 已上传win的编译文件到release,可以自取 14 | 15 | ## Install 16 | 17 | ```bash 18 | cargo install serialport_monitor 19 | ``` 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serialport_monitor" 3 | version = "1.1.0" 4 | description = "monitor and control serialport in terminal" 5 | authors = ["chenxuuu "] 6 | edition = "2021" 7 | repository = "https://github.com/chenxuuu/serialport_monitor" 8 | homepage = "https://github.com/chenxuuu/serialport_monitor" 9 | keywords = ["serialport", "cli"] 10 | license = "MIT" 11 | exclude = [ 12 | "img/*", 13 | ] 14 | 15 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 16 | 17 | [dependencies] 18 | serialport = "4" 19 | time="0.3" 20 | clap = { version = "4", features = ["derive"] } 21 | crossterm = "0" 22 | ctrlc = "3" 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 chenxuuu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/uart_readout.rs: -------------------------------------------------------------------------------- 1 | use std::{sync::mpsc::Sender, io::Read, thread}; 2 | use serialport::SerialPort; 3 | use crate::args_parse::Args; 4 | 5 | //读取串口数据的线程 6 | pub fn read_loop(tx: Sender>, args : &Args) -> Box { 7 | let mut port = serialport::new(&args.port, args.baud_rate) 8 | .timeout(std::time::Duration::from_millis(10)).flow_control(serialport::FlowControl::None) 9 | .open().unwrap(); 10 | port.write_request_to_send(args.rts != 0).unwrap(); 11 | port.write_data_terminal_ready(args.dtr != 0).unwrap(); 12 | let mut buff : [u8;4096] = [0;4096]; 13 | let mut port_loop = port.try_clone().unwrap(); 14 | thread::spawn(move || { 15 | loop { 16 | let len = match port_loop.read(&mut buff) { 17 | Err(e) if e.kind() == std::io::ErrorKind::TimedOut => 0, 18 | Err(e) => { 19 | println!("error: {:?}", e); 20 | tx.send(vec![].to_vec()).unwrap();//发送空数据,表示串口异常 21 | 0 22 | }, 23 | Ok(e) => e, 24 | }; 25 | //没数据,不往下跑 26 | if len == 0 { 27 | continue; 28 | } 29 | tx.send(buff[0..len].to_vec()).unwrap(); 30 | } 31 | }); 32 | port 33 | } 34 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{io::{self, Write}, sync::mpsc}; 2 | 3 | use crate::{uart_readout::read_loop, utf8_buffer::Utf8Buffer}; 4 | mod args_parse; 5 | mod uart_readout; 6 | mod key_listener; 7 | mod utf8_buffer; 8 | 9 | fn main() { 10 | let args = args_parse::get_args(); 11 | 12 | let (tx, rx) = mpsc::channel(); 13 | 14 | //清屏 15 | print!("\x1bc"); 16 | io::stdout().flush().unwrap(); 17 | 18 | let port = read_loop(tx.clone(), &args); 19 | 20 | key_listener::listen_keys(port); 21 | 22 | let mut last_buffer = vec![0u8; 20]; 23 | 24 | loop { 25 | let recv = rx.recv().unwrap(); 26 | if recv.len() == 0 {//串口异常 27 | println!("serial port error! exit!"); 28 | break; 29 | } 30 | let recv = ([last_buffer.clone(),recv]).concat(); 31 | last_buffer.clear(); 32 | 33 | //看看末尾有没有不完整的utf8字符 34 | let mut u8b = Utf8Buffer::new(); 35 | u8b.push_bytes(recv.clone()); 36 | let income_count = u8b.incomplete_bytes_len(); 37 | let s = if income_count > 0 { 38 | last_buffer.extend_from_slice(&recv[recv.len() - income_count..]); 39 | String::from_utf8_lossy(&recv[..recv.len() - income_count]).to_string() 40 | } else { 41 | String::from_utf8_lossy(&recv).to_string() 42 | }; 43 | 44 | io::stdout().write(s.as_bytes()).unwrap(); 45 | io::stdout().flush().ok(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/args_parse.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | #[derive(Parser, Debug)] 4 | #[clap()] 5 | pub struct Args { 6 | ///port name, example COM1,/dev/ttyUSB0 7 | #[clap(short, long, value_parser,value_name="port")] 8 | pub port: String, 9 | ///baud_rate; default 115200 10 | #[clap(short, long, value_parser,default_value_t= 115200)] 11 | pub baud_rate: u32, 12 | ///RTS status; 0 disable, 1 enable 13 | #[clap(short, long, value_parser, default_value_t = 0)] 14 | pub rts: u8, 15 | ///DTR status; 0 disable, 1 enable 16 | #[clap(short, long, value_parser, default_value_t = 0)] 17 | pub dtr: u8, 18 | } 19 | 20 | //获取参数 21 | pub fn get_args() -> Args { 22 | match Args::try_parse(){ 23 | Ok(a) => a, 24 | Err(e) if e.kind() == clap::error::ErrorKind::DisplayHelp => Args::parse() , 25 | Err(e) if e.kind() == clap::error::ErrorKind::DisplayVersion => Args::parse() , 26 | _ => { 27 | //没给参数,手动获取下 28 | let mut buff = String::new(); 29 | let mut a = Args{ 30 | port: String::new(), 31 | baud_rate: 115200, 32 | rts: 0, 33 | dtr: 0, 34 | }; 35 | println!("your serial ports list:"); 36 | let port_list = serialport::available_ports().unwrap(); 37 | port_list.iter().for_each(|p| println!("{}",p.port_name)); 38 | println!("please select your serial port (default first port if exist):"); 39 | std::io::stdin().read_line(&mut buff).expect("read_line error!"); 40 | a.port = if buff.trim().len() == 0 { 41 | if port_list.len() > 0{ 42 | port_list[0].port_name.clone() 43 | } 44 | else{ 45 | panic!("no port found!") 46 | } 47 | } else { 48 | buff.trim().to_string() 49 | }; 50 | buff.clear(); 51 | 52 | println!("please set a baud rate (default 115200):"); 53 | std::io::stdin().read_line(&mut buff).expect("read_line error!"); 54 | if buff.trim().len() != 0 { 55 | a.baud_rate = buff.trim().parse().unwrap(); 56 | } 57 | buff.clear(); 58 | println!("please set rts status (default 0, disable):"); 59 | std::io::stdin().read_line(&mut buff).expect("read_line error!"); 60 | if buff.trim().len() != 0 { 61 | a.rts = buff.trim().parse().unwrap(); 62 | } 63 | buff.clear(); 64 | println!("please set dtr status (default 0, enable):"); 65 | std::io::stdin().read_line(&mut buff).expect("read_line error!"); 66 | if buff.trim().len() != 0 { 67 | a.dtr = buff.trim().parse().unwrap(); 68 | } 69 | a 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/utf8_buffer.rs: -------------------------------------------------------------------------------- 1 | use std::str; 2 | 3 | pub struct Utf8Buffer { 4 | incomplete_bytes: Vec, 5 | } 6 | 7 | impl Utf8Buffer { 8 | pub fn new() -> Self { 9 | Self { 10 | incomplete_bytes: Vec::new(), 11 | } 12 | } 13 | 14 | pub fn push_bytes(&mut self, mut new_bytes: Vec) -> String { 15 | // 将之前不完整的字节与新数据合并 16 | if !self.incomplete_bytes.is_empty() { 17 | let mut combined = std::mem::take(&mut self.incomplete_bytes); 18 | combined.append(&mut new_bytes); 19 | new_bytes = combined; 20 | } 21 | 22 | // 找到最后一个完整的UTF-8字符的位置 23 | let (complete_end, incomplete_start) = self.find_complete_utf8_boundary(&new_bytes); 24 | 25 | // 提取完整的部分 26 | let complete_bytes = &new_bytes[..complete_end]; 27 | let result = String::from_utf8_lossy(complete_bytes).into_owned(); 28 | 29 | // 保存不完整的部分供下次使用 30 | self.incomplete_bytes = new_bytes[incomplete_start..].to_vec(); 31 | 32 | result 33 | } 34 | 35 | fn find_complete_utf8_boundary(&self, bytes: &[u8]) -> (usize, usize) { 36 | if bytes.is_empty() { 37 | return (0, 0); 38 | } 39 | 40 | // 从末尾开始,寻找最后一个完整UTF-8字符的边界 41 | // UTF-8字符最长4字节,所以我们检查最后4个字节的所有可能位置 42 | for i in (bytes.len().saturating_sub(4)..bytes.len()).rev() { 43 | if self.is_utf8_char_boundary(bytes, i) { 44 | // 检查从i开始的部分是否形成完整的UTF-8字符序列 45 | match str::from_utf8(&bytes[i..]) { 46 | Ok(_) => { 47 | // 从i到末尾都是完整的UTF-8 48 | return (bytes.len(), bytes.len()); 49 | } 50 | Err(_) => { 51 | // 从i开始有不完整的字符,所以i是我们要找的边界 52 | return (i, i); 53 | } 54 | } 55 | } 56 | } 57 | 58 | // 如果上面的方法没有找到合适的边界,使用标准方法 59 | match str::from_utf8(bytes) { 60 | Ok(_) => (bytes.len(), bytes.len()), 61 | Err(error) => { 62 | let valid_up_to = error.valid_up_to(); 63 | (valid_up_to, valid_up_to) 64 | } 65 | } 66 | } 67 | 68 | // 检查位置i是否是UTF-8字符边界 69 | fn is_utf8_char_boundary(&self, bytes: &[u8], i: usize) -> bool { 70 | if i == 0 || i >= bytes.len() { 71 | return true; 72 | } 73 | 74 | let byte = bytes[i]; 75 | // UTF-8字符边界: 76 | // - ASCII字符 (0xxxxxxx) 77 | // - 多字节字符的开始 (11xxxxxx) 78 | // 不是延续字节 (10xxxxxx) 79 | (byte & 0x80) == 0 || (byte & 0xC0) == 0xC0 80 | } 81 | 82 | pub fn incomplete_bytes_len(&self) -> usize { 83 | self.incomplete_bytes.len() 84 | } 85 | 86 | } 87 | 88 | 89 | #[cfg(test)] 90 | mod tests { 91 | use super::*; 92 | 93 | #[test] 94 | fn test_valid_mixed_up_to_usage() { 95 | fn is_char_boundary_at(bytes: &[u8], i: usize) -> bool { 96 | if i == 0 || i >= bytes.len() { 97 | return true; 98 | } 99 | 100 | let byte = bytes[i]; 101 | // UTF-8字符边界检查 102 | (byte & 0x80) == 0 || (byte & 0xC0) == 0xC0 103 | } 104 | // 辅助函数:用于验证UTF-8处理逻辑 105 | pub fn find_last_complete_utf8_boundary(bytes: &[u8]) -> usize { 106 | if bytes.is_empty() { 107 | return 0; 108 | } 109 | 110 | // 从末尾向前查找,最多检查4个字节(UTF-8最大字符长度) 111 | let start_check = bytes.len().saturating_sub(4); 112 | 113 | for i in (start_check..bytes.len()).rev() { 114 | // 检查这是否是字符边界 115 | if is_char_boundary_at(bytes, i) { 116 | // 验证从i到末尾是否都是有效UTF-8 117 | if str::from_utf8(&bytes[i..]).is_ok() { 118 | return bytes.len(); 119 | } else { 120 | return i; 121 | } 122 | } 123 | } 124 | 125 | // 如果没找到合适的边界,使用标准方法 126 | match str::from_utf8(bytes) { 127 | Ok(_) => bytes.len(), 128 | Err(error) => error.valid_up_to(), 129 | } 130 | } 131 | 132 | let mixed_data = vec![ 133 | 0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello" (0-4) 134 | 0xE4, 0xBD, // "你"的前两个字节 (5-6) 不完整 135 | 0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello" (7-11) 136 | 0xE4, 0xBD, // "你"的前两个字节 (12-13) 不完整 137 | ]; 138 | 139 | // 测试我们的边界查找函数 140 | let boundary = find_last_complete_utf8_boundary(&mixed_data); 141 | assert_eq!(boundary, 12); 142 | println!("找到的边界位置: {}", boundary); 143 | 144 | // 验证前12个字节确实是有效的UTF-8 145 | if let Ok(s) = str::from_utf8(&mixed_data[..12]) { 146 | println!("前12个字节的内容: '{}'", s); 147 | assert_eq!(s, "HelloHello"); 148 | } 149 | } 150 | 151 | #[test] 152 | fn test_standard_utf8_error() { 153 | let mixed_data = vec![ 154 | 0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello" 155 | 0xE4, 0xBD, // "你"的前两个字节 (不完整) 156 | 0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello" 157 | 0xE4, 0xBD, // "你"的前两个字节 (不完整) 158 | ]; 159 | 160 | // 标准的UTF-8错误处理 161 | match str::from_utf8(&mixed_data) { 162 | Ok(_) => panic!("应该失败"), 163 | Err(error) => { 164 | println!("标准 valid_up_to: {}", error.valid_up_to()); 165 | // 标准方法会在第一个错误处停止,返回5 166 | assert_eq!(error.valid_up_to(), 5); 167 | } 168 | } 169 | } 170 | 171 | #[test] 172 | fn test_buffer_with_mixed_data() { 173 | let mut buffer = Utf8Buffer::new(); 174 | 175 | let mixed_data = vec![ 176 | 0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello" 177 | 0xE4, 0xBD, // 不完整的"你" 178 | ]; 179 | 180 | let result = buffer.push_bytes(mixed_data); 181 | assert_eq!(result, "Hello"); 182 | assert_eq!(buffer.incomplete_bytes_len(), 2); // 缓存不完整的字节 183 | 184 | // 添加剩余的字节来完成"你" 185 | let remaining = vec![0xA0]; // "你"的最后一个字节 186 | let result2 = buffer.push_bytes(remaining); 187 | assert_eq!(result2, "你"); 188 | assert_eq!(buffer.incomplete_bytes_len(), 0); 189 | } 190 | 191 | #[test] 192 | fn test_multiple_incomplete_sequences() { 193 | let mut buffer = Utf8Buffer::new(); 194 | 195 | // 包含多个完整字符和一个不完整字符的数据 196 | let data = vec![ 197 | 0x48, 0x65, 0x6C, 0x6C, 0x6F, // "Hello" 198 | 0xE4, 0xBD, 0xA0, // "你" (完整) 199 | 0x48, 0x69, // "Hi" 200 | 0xE4, 0xBD, // "你"的前两个字节 (不完整) 201 | ]; 202 | 203 | let result = buffer.push_bytes(data); 204 | assert_eq!(result, "Hello你Hi"); 205 | assert_eq!(buffer.incomplete_bytes_len(), 2); 206 | } 207 | } -------------------------------------------------------------------------------- /src/key_listener.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | use crossterm::event::{Event, KeyCode, KeyEventKind, KeyModifiers}; 4 | use serialport::SerialPort; 5 | 6 | pub fn listen_keys(mut port: Box) { 7 | //ctrl+c单独处理 8 | let mut ctrlc_port = port.try_clone().unwrap(); 9 | ctrlc::set_handler(move || { 10 | ctrlc_port.write(&get_key_data(KeyCode::Char('c'), true)).unwrap(); 11 | }) 12 | .expect("Error setting Ctrl-C handler"); 13 | //port.write(&get_special_key_codes("[?1;0c")).unwrap(); 14 | 15 | thread::spawn(move || { 16 | loop { 17 | let mut code = vec![]; 18 | // `read()` blocks until an `Event` is available 19 | match crossterm::event::read().unwrap() { 20 | //按键事件 21 | Event::Key(event) => { 22 | if event.kind == KeyEventKind::Press { 23 | code = get_key_data(event.code, event.modifiers == KeyModifiers::CONTROL); 24 | } 25 | } 26 | // Event::Resize(columns, _) => { 27 | // code = get_special_key_codes( 28 | // if columns >= 132 { 29 | // "[?3h" 30 | // } else { 31 | // "[?3l" 32 | // } 33 | // ); 34 | // }, 35 | _ => (), 36 | }; 37 | if code.len() > 0 { 38 | port.write(&code).unwrap(); 39 | } 40 | } 41 | }); 42 | } 43 | 44 | //返回按键数据 45 | fn get_key_data(key: KeyCode, ctrl: bool) -> Vec { 46 | match key { 47 | //数据来源:https://redirect.cs.umbc.edu/portal/help/theory/ascii.txt 48 | //ctrl组合键 49 | KeyCode::Char('@') if ctrl => vec![0x00], 50 | KeyCode::Char(' ') if ctrl => vec![0x00], 51 | KeyCode::Char('A') | KeyCode::Char('a') if ctrl => vec![0x01], 52 | KeyCode::Char('B') | KeyCode::Char('b') if ctrl => vec![0x02], 53 | KeyCode::Char('C') | KeyCode::Char('c') if ctrl => vec![0x03], 54 | KeyCode::Char('D') | KeyCode::Char('d') if ctrl => vec![0x04], 55 | KeyCode::Char('E') | KeyCode::Char('e') if ctrl => vec![0x05], 56 | KeyCode::Char('F') | KeyCode::Char('f') if ctrl => vec![0x06], 57 | KeyCode::Char('G') | KeyCode::Char('g') if ctrl => vec![0x07], 58 | KeyCode::Char('H') | KeyCode::Char('h') if ctrl => vec![0x08], 59 | KeyCode::Char('I') | KeyCode::Char('i') if ctrl => vec![0x09], 60 | KeyCode::Char('J') | KeyCode::Char('j') if ctrl => vec![0x0A], 61 | KeyCode::Char('K') | KeyCode::Char('k') if ctrl => vec![0x0B], 62 | KeyCode::Char('L') | KeyCode::Char('l') if ctrl => vec![0x0C], 63 | KeyCode::Char('M') | KeyCode::Char('m') if ctrl => vec![0x0D], 64 | KeyCode::Char('N') | KeyCode::Char('n') if ctrl => vec![0x0E], 65 | KeyCode::Char('O') | KeyCode::Char('o') if ctrl => vec![0x0F], 66 | KeyCode::Char('P') | KeyCode::Char('p') if ctrl => vec![0x10], 67 | KeyCode::Char('Q') | KeyCode::Char('q') if ctrl => vec![0x11], 68 | KeyCode::Char('R') | KeyCode::Char('r') if ctrl => vec![0x12], 69 | KeyCode::Char('S') | KeyCode::Char('s') if ctrl => vec![0x13], 70 | KeyCode::Char('T') | KeyCode::Char('t') if ctrl => vec![0x14], 71 | KeyCode::Char('U') | KeyCode::Char('u') if ctrl => vec![0x15], 72 | KeyCode::Char('V') | KeyCode::Char('v') if ctrl => vec![0x16], 73 | KeyCode::Char('W') | KeyCode::Char('w') if ctrl => vec![0x17], 74 | KeyCode::Char('X') | KeyCode::Char('x') if ctrl => vec![0x18], 75 | KeyCode::Char('Y') | KeyCode::Char('y') if ctrl => vec![0x19], 76 | KeyCode::Char('Z') | KeyCode::Char('z') if ctrl => vec![0x1A], 77 | KeyCode::Char('[') if ctrl => vec![0x1B], 78 | KeyCode::Char('\\') if ctrl => vec![0x1C], 79 | KeyCode::Char(']') if ctrl => vec![0x1D], 80 | KeyCode::Char('^') if ctrl => vec![0x1E], 81 | KeyCode::Char('_') if ctrl => vec![0x1F], 82 | //普通字符按键 83 | KeyCode::Char(' ') => vec![0x20], 84 | KeyCode::Char('!') => vec![0x21], 85 | KeyCode::Char('"') => vec![0x22], 86 | KeyCode::Char('#') => vec![0x23], 87 | KeyCode::Char('$') => vec![0x24], 88 | KeyCode::Char('%') => vec![0x25], 89 | KeyCode::Char('&') => vec![0x26], 90 | KeyCode::Char('\'') => vec![0x27], 91 | KeyCode::Char('(') => vec![0x28], 92 | KeyCode::Char(')') => vec![0x29], 93 | KeyCode::Char('*') => vec![0x2A], 94 | KeyCode::Char('+') => vec![0x2B], 95 | KeyCode::Char(',') => vec![0x2C], 96 | KeyCode::Char('-') => vec![0x2D], 97 | KeyCode::Char('.') => vec![0x2E], 98 | KeyCode::Char('/') => vec![0x2F], 99 | KeyCode::Char('0') => vec![0x30], 100 | KeyCode::Char('1') => vec![0x31], 101 | KeyCode::Char('2') => vec![0x32], 102 | KeyCode::Char('3') => vec![0x33], 103 | KeyCode::Char('4') => vec![0x34], 104 | KeyCode::Char('5') => vec![0x35], 105 | KeyCode::Char('6') => vec![0x36], 106 | KeyCode::Char('7') => vec![0x37], 107 | KeyCode::Char('8') => vec![0x38], 108 | KeyCode::Char('9') => vec![0x39], 109 | KeyCode::Char(':') => vec![0x3A], 110 | KeyCode::Char(';') => vec![0x3B], 111 | KeyCode::Char('<') => vec![0x3C], 112 | KeyCode::Char('=') => vec![0x3D], 113 | KeyCode::Char('>') => vec![0x3E], 114 | KeyCode::Char('?') => vec![0x3F], 115 | KeyCode::Char('@') => vec![0x40], 116 | KeyCode::Char('A') => vec![0x41], 117 | KeyCode::Char('B') => vec![0x42], 118 | KeyCode::Char('C') => vec![0x43], 119 | KeyCode::Char('D') => vec![0x44], 120 | KeyCode::Char('E') => vec![0x45], 121 | KeyCode::Char('F') => vec![0x46], 122 | KeyCode::Char('G') => vec![0x47], 123 | KeyCode::Char('H') => vec![0x48], 124 | KeyCode::Char('I') => vec![0x49], 125 | KeyCode::Char('J') => vec![0x4A], 126 | KeyCode::Char('K') => vec![0x4B], 127 | KeyCode::Char('L') => vec![0x4C], 128 | KeyCode::Char('M') => vec![0x4D], 129 | KeyCode::Char('N') => vec![0x4E], 130 | KeyCode::Char('O') => vec![0x4F], 131 | KeyCode::Char('P') => vec![0x50], 132 | KeyCode::Char('Q') => vec![0x51], 133 | KeyCode::Char('R') => vec![0x52], 134 | KeyCode::Char('S') => vec![0x53], 135 | KeyCode::Char('T') => vec![0x54], 136 | KeyCode::Char('U') => vec![0x55], 137 | KeyCode::Char('V') => vec![0x56], 138 | KeyCode::Char('W') => vec![0x57], 139 | KeyCode::Char('X') => vec![0x58], 140 | KeyCode::Char('Y') => vec![0x59], 141 | KeyCode::Char('Z') => vec![0x5A], 142 | KeyCode::Char('[') => vec![0x5B], 143 | KeyCode::Char('\\') => vec![0x5C], 144 | KeyCode::Char(']') => vec![0x5D], 145 | KeyCode::Char('^') => vec![0x5E], 146 | KeyCode::Char('_') => vec![0x5F], 147 | KeyCode::Char('`') => vec![0x60], 148 | KeyCode::Char('a') => vec![0x61], 149 | KeyCode::Char('b') => vec![0x62], 150 | KeyCode::Char('c') => vec![0x63], 151 | KeyCode::Char('d') => vec![0x64], 152 | KeyCode::Char('e') => vec![0x65], 153 | KeyCode::Char('f') => vec![0x66], 154 | KeyCode::Char('g') => vec![0x67], 155 | KeyCode::Char('h') => vec![0x68], 156 | KeyCode::Char('i') => vec![0x69], 157 | KeyCode::Char('j') => vec![0x6A], 158 | KeyCode::Char('k') => vec![0x6B], 159 | KeyCode::Char('l') => vec![0x6C], 160 | KeyCode::Char('m') => vec![0x6D], 161 | KeyCode::Char('n') => vec![0x6E], 162 | KeyCode::Char('o') => vec![0x6F], 163 | KeyCode::Char('p') => vec![0x70], 164 | KeyCode::Char('q') => vec![0x71], 165 | KeyCode::Char('r') => vec![0x72], 166 | KeyCode::Char('s') => vec![0x73], 167 | KeyCode::Char('t') => vec![0x74], 168 | KeyCode::Char('u') => vec![0x75], 169 | KeyCode::Char('v') => vec![0x76], 170 | KeyCode::Char('w') => vec![0x77], 171 | KeyCode::Char('x') => vec![0x78], 172 | KeyCode::Char('y') => vec![0x79], 173 | KeyCode::Char('z') => vec![0x7A], 174 | KeyCode::Char('{') => vec![0x7B], 175 | KeyCode::Char('|') => vec![0x7C], 176 | KeyCode::Char('}') => vec![0x7D], 177 | KeyCode::Char('~') => vec![0x7E], 178 | //中文啥的 179 | KeyCode::Char(any) => any.to_string().as_bytes().to_vec(), 180 | 181 | //特殊按键 182 | KeyCode::Delete => vec![0x7F], 183 | KeyCode::Backspace => vec![0x08], 184 | KeyCode::Tab => vec![0x09], 185 | KeyCode::Enter => vec![0x0D], 186 | KeyCode::Pause=> vec![0x1A], 187 | KeyCode::Esc => vec![0x1B], 188 | 189 | // https://learn.microsoft.com/zh-cn/windows/console/console-virtual-terminal-sequences 190 | KeyCode::Up if ctrl => get_special_key_codes("[1;5A"), 191 | KeyCode::Down if ctrl => get_special_key_codes("[1;5B"), 192 | KeyCode::Right if ctrl => get_special_key_codes("[1;5C"), 193 | KeyCode::Left if ctrl => get_special_key_codes("[1;5D"), 194 | KeyCode::Up => get_special_key_codes("[A"), 195 | KeyCode::Down => get_special_key_codes("[B"), 196 | KeyCode::Right => get_special_key_codes("[C"), 197 | KeyCode::Left => get_special_key_codes("[D"), 198 | KeyCode::Home => get_special_key_codes("[H"), 199 | KeyCode::End => get_special_key_codes("[F"), 200 | KeyCode::Insert => get_special_key_codes("[2~"), 201 | KeyCode::PageUp => get_special_key_codes("[5~"), 202 | KeyCode::PageDown => get_special_key_codes("[6~"), 203 | KeyCode::F(1) => get_special_key_codes("OP"), 204 | KeyCode::F(2) => get_special_key_codes("OQ"), 205 | KeyCode::F(3) => get_special_key_codes("OR"), 206 | KeyCode::F(4) => get_special_key_codes("OS"), 207 | KeyCode::F(5) => get_special_key_codes("[15~"), 208 | KeyCode::F(6) => get_special_key_codes("[17~"), 209 | KeyCode::F(7) => get_special_key_codes("[18~"), 210 | KeyCode::F(8) => get_special_key_codes("[19~"), 211 | KeyCode::F(9) => get_special_key_codes("[20~"), 212 | KeyCode::F(10) => get_special_key_codes("[21~"), 213 | KeyCode::F(11) => get_special_key_codes("[23~"), 214 | KeyCode::F(12) => get_special_key_codes("[24~"), 215 | 216 | _ => vec![], //其他按键不处理 217 | } 218 | } 219 | 220 | fn get_special_key_codes(s : &str) -> Vec { 221 | let mut v = vec![0x1B]; 222 | let mut b = s.as_bytes().to_vec(); 223 | v.append(&mut b); 224 | v 225 | } 226 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.19" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.7" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.3" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" 40 | dependencies = [ 41 | "windows-sys 0.59.0", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.9" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" 49 | dependencies = [ 50 | "anstyle", 51 | "once_cell_polyfill", 52 | "windows-sys 0.59.0", 53 | ] 54 | 55 | [[package]] 56 | name = "autocfg" 57 | version = "1.5.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 60 | 61 | [[package]] 62 | name = "bitflags" 63 | version = "1.3.2" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 66 | 67 | [[package]] 68 | name = "bitflags" 69 | version = "2.9.1" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" 72 | 73 | [[package]] 74 | name = "cfg-if" 75 | version = "1.0.1" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" 78 | 79 | [[package]] 80 | name = "cfg_aliases" 81 | version = "0.2.1" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 84 | 85 | [[package]] 86 | name = "clap" 87 | version = "4.5.41" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" 90 | dependencies = [ 91 | "clap_builder", 92 | "clap_derive", 93 | ] 94 | 95 | [[package]] 96 | name = "clap_builder" 97 | version = "4.5.41" 98 | source = "registry+https://github.com/rust-lang/crates.io-index" 99 | checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" 100 | dependencies = [ 101 | "anstream", 102 | "anstyle", 103 | "clap_lex", 104 | "strsim", 105 | ] 106 | 107 | [[package]] 108 | name = "clap_derive" 109 | version = "4.5.41" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" 112 | dependencies = [ 113 | "heck", 114 | "proc-macro2", 115 | "quote", 116 | "syn", 117 | ] 118 | 119 | [[package]] 120 | name = "clap_lex" 121 | version = "0.7.5" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" 124 | 125 | [[package]] 126 | name = "colorchoice" 127 | version = "1.0.4" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 130 | 131 | [[package]] 132 | name = "convert_case" 133 | version = "0.7.1" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" 136 | dependencies = [ 137 | "unicode-segmentation", 138 | ] 139 | 140 | [[package]] 141 | name = "core-foundation" 142 | version = "0.10.1" 143 | source = "registry+https://github.com/rust-lang/crates.io-index" 144 | checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 145 | dependencies = [ 146 | "core-foundation-sys", 147 | "libc", 148 | ] 149 | 150 | [[package]] 151 | name = "core-foundation-sys" 152 | version = "0.8.7" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 155 | 156 | [[package]] 157 | name = "crossterm" 158 | version = "0.29.0" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" 161 | dependencies = [ 162 | "bitflags 2.9.1", 163 | "crossterm_winapi", 164 | "derive_more", 165 | "document-features", 166 | "mio", 167 | "parking_lot", 168 | "rustix", 169 | "signal-hook", 170 | "signal-hook-mio", 171 | "winapi", 172 | ] 173 | 174 | [[package]] 175 | name = "crossterm_winapi" 176 | version = "0.9.1" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 179 | dependencies = [ 180 | "winapi", 181 | ] 182 | 183 | [[package]] 184 | name = "ctrlc" 185 | version = "3.4.7" 186 | source = "registry+https://github.com/rust-lang/crates.io-index" 187 | checksum = "46f93780a459b7d656ef7f071fe699c4d3d2cb201c4b24d085b6ddc505276e73" 188 | dependencies = [ 189 | "nix 0.30.1", 190 | "windows-sys 0.59.0", 191 | ] 192 | 193 | [[package]] 194 | name = "deranged" 195 | version = "0.4.0" 196 | source = "registry+https://github.com/rust-lang/crates.io-index" 197 | checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" 198 | dependencies = [ 199 | "powerfmt", 200 | ] 201 | 202 | [[package]] 203 | name = "derive_more" 204 | version = "2.0.1" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" 207 | dependencies = [ 208 | "derive_more-impl", 209 | ] 210 | 211 | [[package]] 212 | name = "derive_more-impl" 213 | version = "2.0.1" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" 216 | dependencies = [ 217 | "convert_case", 218 | "proc-macro2", 219 | "quote", 220 | "syn", 221 | ] 222 | 223 | [[package]] 224 | name = "document-features" 225 | version = "0.2.11" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d" 228 | dependencies = [ 229 | "litrs", 230 | ] 231 | 232 | [[package]] 233 | name = "errno" 234 | version = "0.3.13" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" 237 | dependencies = [ 238 | "libc", 239 | "windows-sys 0.60.2", 240 | ] 241 | 242 | [[package]] 243 | name = "heck" 244 | version = "0.5.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 247 | 248 | [[package]] 249 | name = "io-kit-sys" 250 | version = "0.4.1" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" 253 | dependencies = [ 254 | "core-foundation-sys", 255 | "mach2", 256 | ] 257 | 258 | [[package]] 259 | name = "is_terminal_polyfill" 260 | version = "1.70.1" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 263 | 264 | [[package]] 265 | name = "libc" 266 | version = "0.2.174" 267 | source = "registry+https://github.com/rust-lang/crates.io-index" 268 | checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" 269 | 270 | [[package]] 271 | name = "libudev" 272 | version = "0.3.0" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "78b324152da65df7bb95acfcaab55e3097ceaab02fb19b228a9eb74d55f135e0" 275 | dependencies = [ 276 | "libc", 277 | "libudev-sys", 278 | ] 279 | 280 | [[package]] 281 | name = "libudev-sys" 282 | version = "0.1.4" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" 285 | dependencies = [ 286 | "libc", 287 | "pkg-config", 288 | ] 289 | 290 | [[package]] 291 | name = "linux-raw-sys" 292 | version = "0.9.4" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" 295 | 296 | [[package]] 297 | name = "litrs" 298 | version = "0.4.1" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" 301 | 302 | [[package]] 303 | name = "lock_api" 304 | version = "0.4.13" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" 307 | dependencies = [ 308 | "autocfg", 309 | "scopeguard", 310 | ] 311 | 312 | [[package]] 313 | name = "log" 314 | version = "0.4.27" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 317 | 318 | [[package]] 319 | name = "mach2" 320 | version = "0.4.3" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" 323 | dependencies = [ 324 | "libc", 325 | ] 326 | 327 | [[package]] 328 | name = "mio" 329 | version = "1.0.4" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" 332 | dependencies = [ 333 | "libc", 334 | "log", 335 | "wasi", 336 | "windows-sys 0.59.0", 337 | ] 338 | 339 | [[package]] 340 | name = "nix" 341 | version = "0.26.4" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 344 | dependencies = [ 345 | "bitflags 1.3.2", 346 | "cfg-if", 347 | "libc", 348 | ] 349 | 350 | [[package]] 351 | name = "nix" 352 | version = "0.30.1" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" 355 | dependencies = [ 356 | "bitflags 2.9.1", 357 | "cfg-if", 358 | "cfg_aliases", 359 | "libc", 360 | ] 361 | 362 | [[package]] 363 | name = "num-conv" 364 | version = "0.1.0" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 367 | 368 | [[package]] 369 | name = "once_cell_polyfill" 370 | version = "1.70.1" 371 | source = "registry+https://github.com/rust-lang/crates.io-index" 372 | checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" 373 | 374 | [[package]] 375 | name = "parking_lot" 376 | version = "0.12.4" 377 | source = "registry+https://github.com/rust-lang/crates.io-index" 378 | checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" 379 | dependencies = [ 380 | "lock_api", 381 | "parking_lot_core", 382 | ] 383 | 384 | [[package]] 385 | name = "parking_lot_core" 386 | version = "0.9.11" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" 389 | dependencies = [ 390 | "cfg-if", 391 | "libc", 392 | "redox_syscall", 393 | "smallvec", 394 | "windows-targets 0.52.6", 395 | ] 396 | 397 | [[package]] 398 | name = "pkg-config" 399 | version = "0.3.32" 400 | source = "registry+https://github.com/rust-lang/crates.io-index" 401 | checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 402 | 403 | [[package]] 404 | name = "powerfmt" 405 | version = "0.2.0" 406 | source = "registry+https://github.com/rust-lang/crates.io-index" 407 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 408 | 409 | [[package]] 410 | name = "proc-macro2" 411 | version = "1.0.95" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 414 | dependencies = [ 415 | "unicode-ident", 416 | ] 417 | 418 | [[package]] 419 | name = "quote" 420 | version = "1.0.40" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 423 | dependencies = [ 424 | "proc-macro2", 425 | ] 426 | 427 | [[package]] 428 | name = "redox_syscall" 429 | version = "0.5.15" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "7e8af0dde094006011e6a740d4879319439489813bd0bcdc7d821beaeeff48ec" 432 | dependencies = [ 433 | "bitflags 2.9.1", 434 | ] 435 | 436 | [[package]] 437 | name = "rustix" 438 | version = "1.0.8" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" 441 | dependencies = [ 442 | "bitflags 2.9.1", 443 | "errno", 444 | "libc", 445 | "linux-raw-sys", 446 | "windows-sys 0.60.2", 447 | ] 448 | 449 | [[package]] 450 | name = "scopeguard" 451 | version = "1.2.0" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 454 | 455 | [[package]] 456 | name = "serde" 457 | version = "1.0.219" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 460 | dependencies = [ 461 | "serde_derive", 462 | ] 463 | 464 | [[package]] 465 | name = "serde_derive" 466 | version = "1.0.219" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 469 | dependencies = [ 470 | "proc-macro2", 471 | "quote", 472 | "syn", 473 | ] 474 | 475 | [[package]] 476 | name = "serialport" 477 | version = "4.7.2" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "cdb0bc984f6af6ef8bab54e6cf2071579ee75b9286aa9f2319a0d220c28b0a2b" 480 | dependencies = [ 481 | "bitflags 2.9.1", 482 | "cfg-if", 483 | "core-foundation", 484 | "core-foundation-sys", 485 | "io-kit-sys", 486 | "libudev", 487 | "mach2", 488 | "nix 0.26.4", 489 | "scopeguard", 490 | "unescaper", 491 | "winapi", 492 | ] 493 | 494 | [[package]] 495 | name = "serialport_monitor" 496 | version = "1.1.0" 497 | dependencies = [ 498 | "clap", 499 | "crossterm", 500 | "ctrlc", 501 | "serialport", 502 | "time", 503 | ] 504 | 505 | [[package]] 506 | name = "signal-hook" 507 | version = "0.3.18" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" 510 | dependencies = [ 511 | "libc", 512 | "signal-hook-registry", 513 | ] 514 | 515 | [[package]] 516 | name = "signal-hook-mio" 517 | version = "0.2.4" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" 520 | dependencies = [ 521 | "libc", 522 | "mio", 523 | "signal-hook", 524 | ] 525 | 526 | [[package]] 527 | name = "signal-hook-registry" 528 | version = "1.4.5" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" 531 | dependencies = [ 532 | "libc", 533 | ] 534 | 535 | [[package]] 536 | name = "smallvec" 537 | version = "1.15.1" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 540 | 541 | [[package]] 542 | name = "strsim" 543 | version = "0.11.1" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 546 | 547 | [[package]] 548 | name = "syn" 549 | version = "2.0.104" 550 | source = "registry+https://github.com/rust-lang/crates.io-index" 551 | checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" 552 | dependencies = [ 553 | "proc-macro2", 554 | "quote", 555 | "unicode-ident", 556 | ] 557 | 558 | [[package]] 559 | name = "thiserror" 560 | version = "2.0.12" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" 563 | dependencies = [ 564 | "thiserror-impl", 565 | ] 566 | 567 | [[package]] 568 | name = "thiserror-impl" 569 | version = "2.0.12" 570 | source = "registry+https://github.com/rust-lang/crates.io-index" 571 | checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" 572 | dependencies = [ 573 | "proc-macro2", 574 | "quote", 575 | "syn", 576 | ] 577 | 578 | [[package]] 579 | name = "time" 580 | version = "0.3.41" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" 583 | dependencies = [ 584 | "deranged", 585 | "num-conv", 586 | "powerfmt", 587 | "serde", 588 | "time-core", 589 | ] 590 | 591 | [[package]] 592 | name = "time-core" 593 | version = "0.1.4" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" 596 | 597 | [[package]] 598 | name = "unescaper" 599 | version = "0.1.6" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "c01d12e3a56a4432a8b436f293c25f4808bdf9e9f9f98f9260bba1f1bc5a1f26" 602 | dependencies = [ 603 | "thiserror", 604 | ] 605 | 606 | [[package]] 607 | name = "unicode-ident" 608 | version = "1.0.18" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 611 | 612 | [[package]] 613 | name = "unicode-segmentation" 614 | version = "1.12.0" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 617 | 618 | [[package]] 619 | name = "utf8parse" 620 | version = "0.2.2" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 623 | 624 | [[package]] 625 | name = "wasi" 626 | version = "0.11.1+wasi-snapshot-preview1" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 629 | 630 | [[package]] 631 | name = "winapi" 632 | version = "0.3.9" 633 | source = "registry+https://github.com/rust-lang/crates.io-index" 634 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 635 | dependencies = [ 636 | "winapi-i686-pc-windows-gnu", 637 | "winapi-x86_64-pc-windows-gnu", 638 | ] 639 | 640 | [[package]] 641 | name = "winapi-i686-pc-windows-gnu" 642 | version = "0.4.0" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 645 | 646 | [[package]] 647 | name = "winapi-x86_64-pc-windows-gnu" 648 | version = "0.4.0" 649 | source = "registry+https://github.com/rust-lang/crates.io-index" 650 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 651 | 652 | [[package]] 653 | name = "windows-sys" 654 | version = "0.59.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 657 | dependencies = [ 658 | "windows-targets 0.52.6", 659 | ] 660 | 661 | [[package]] 662 | name = "windows-sys" 663 | version = "0.60.2" 664 | source = "registry+https://github.com/rust-lang/crates.io-index" 665 | checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" 666 | dependencies = [ 667 | "windows-targets 0.53.2", 668 | ] 669 | 670 | [[package]] 671 | name = "windows-targets" 672 | version = "0.52.6" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 675 | dependencies = [ 676 | "windows_aarch64_gnullvm 0.52.6", 677 | "windows_aarch64_msvc 0.52.6", 678 | "windows_i686_gnu 0.52.6", 679 | "windows_i686_gnullvm 0.52.6", 680 | "windows_i686_msvc 0.52.6", 681 | "windows_x86_64_gnu 0.52.6", 682 | "windows_x86_64_gnullvm 0.52.6", 683 | "windows_x86_64_msvc 0.52.6", 684 | ] 685 | 686 | [[package]] 687 | name = "windows-targets" 688 | version = "0.53.2" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" 691 | dependencies = [ 692 | "windows_aarch64_gnullvm 0.53.0", 693 | "windows_aarch64_msvc 0.53.0", 694 | "windows_i686_gnu 0.53.0", 695 | "windows_i686_gnullvm 0.53.0", 696 | "windows_i686_msvc 0.53.0", 697 | "windows_x86_64_gnu 0.53.0", 698 | "windows_x86_64_gnullvm 0.53.0", 699 | "windows_x86_64_msvc 0.53.0", 700 | ] 701 | 702 | [[package]] 703 | name = "windows_aarch64_gnullvm" 704 | version = "0.52.6" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 707 | 708 | [[package]] 709 | name = "windows_aarch64_gnullvm" 710 | version = "0.53.0" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" 713 | 714 | [[package]] 715 | name = "windows_aarch64_msvc" 716 | version = "0.52.6" 717 | source = "registry+https://github.com/rust-lang/crates.io-index" 718 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 719 | 720 | [[package]] 721 | name = "windows_aarch64_msvc" 722 | version = "0.53.0" 723 | source = "registry+https://github.com/rust-lang/crates.io-index" 724 | checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" 725 | 726 | [[package]] 727 | name = "windows_i686_gnu" 728 | version = "0.52.6" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 731 | 732 | [[package]] 733 | name = "windows_i686_gnu" 734 | version = "0.53.0" 735 | source = "registry+https://github.com/rust-lang/crates.io-index" 736 | checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" 737 | 738 | [[package]] 739 | name = "windows_i686_gnullvm" 740 | version = "0.52.6" 741 | source = "registry+https://github.com/rust-lang/crates.io-index" 742 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 743 | 744 | [[package]] 745 | name = "windows_i686_gnullvm" 746 | version = "0.53.0" 747 | source = "registry+https://github.com/rust-lang/crates.io-index" 748 | checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" 749 | 750 | [[package]] 751 | name = "windows_i686_msvc" 752 | version = "0.52.6" 753 | source = "registry+https://github.com/rust-lang/crates.io-index" 754 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 755 | 756 | [[package]] 757 | name = "windows_i686_msvc" 758 | version = "0.53.0" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" 761 | 762 | [[package]] 763 | name = "windows_x86_64_gnu" 764 | version = "0.52.6" 765 | source = "registry+https://github.com/rust-lang/crates.io-index" 766 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 767 | 768 | [[package]] 769 | name = "windows_x86_64_gnu" 770 | version = "0.53.0" 771 | source = "registry+https://github.com/rust-lang/crates.io-index" 772 | checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" 773 | 774 | [[package]] 775 | name = "windows_x86_64_gnullvm" 776 | version = "0.52.6" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 779 | 780 | [[package]] 781 | name = "windows_x86_64_gnullvm" 782 | version = "0.53.0" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" 785 | 786 | [[package]] 787 | name = "windows_x86_64_msvc" 788 | version = "0.52.6" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 791 | 792 | [[package]] 793 | name = "windows_x86_64_msvc" 794 | version = "0.53.0" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" 797 | --------------------------------------------------------------------------------