├── .gitignore ├── README.md ├── src ├── common │ ├── mod.rs │ └── input.rs ├── main.rs └── ui │ ├── mod.rs │ ├── rxtx.rs │ ├── layout.rs │ ├── demo.rs │ └── index.rs ├── Cargo.toml ├── .github └── workflows │ └── rust.yml └── Cargo.lock /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Function 3 | * RX TX(show TX) 4 | * show HEX 5 | * Command List 6 | * Stream 7 | * Ymodem Send & Receive 8 | * Chart 9 | * modbus rtu -------------------------------------------------------------------------------- /src/common/mod.rs: -------------------------------------------------------------------------------- 1 | use tokio_serial::SerialStream; 2 | 3 | pub mod input; 4 | 5 | // pub fn serial_read(serial:&mut SerialStream)-> Result>{ 6 | 7 | // } 8 | 9 | // pub fn serial_write()-> Result<>{ 10 | 11 | // } 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serial_tool" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | ratatui = "0.27.0" 8 | indoc = "2.0.5" 9 | strum = "0.26" 10 | tokio = { version = "1", features = ["full"] } 11 | tokio-serial = "5.4.1" -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: Build 20 | run: cargo build --verbose 21 | - name: Run tests 22 | run: cargo test --verbose 23 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{error::Error, io}; 2 | 3 | use ratatui::{ 4 | crossterm::{ 5 | event::{DisableMouseCapture, EnableMouseCapture}, 6 | execute, 7 | terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, 8 | }, 9 | prelude::* 10 | }; 11 | use ui::AppContext; 12 | 13 | mod ui; 14 | mod common; 15 | 16 | 17 | 18 | #[tokio::main] 19 | async fn main() -> Result<(), Box> { 20 | // setup terminal 21 | enable_raw_mode()?; 22 | let mut stdout = io::stdout(); 23 | execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; 24 | let backend = CrosstermBackend::new(stdout); 25 | let mut terminal = Terminal::new(backend)?; 26 | 27 | // create app and run it 28 | let res = AppContext::new().run_app(&mut terminal).await; 29 | 30 | // restore terminal 31 | disable_raw_mode()?; 32 | execute!( 33 | terminal.backend_mut(), 34 | LeaveAlternateScreen, 35 | DisableMouseCapture 36 | )?; 37 | terminal.show_cursor()?; 38 | 39 | if let Err(err) = res { 40 | println!("{err:?}"); 41 | } 42 | 43 | Ok(()) 44 | } -------------------------------------------------------------------------------- /src/common/input.rs: -------------------------------------------------------------------------------- 1 | pub struct Input { 2 | buf: String, 3 | character_index: usize, 4 | } 5 | 6 | impl Input { 7 | pub const fn new() -> Self { 8 | Self { 9 | buf: String::new(), 10 | character_index: 0, 11 | } 12 | } 13 | 14 | pub fn get_index(&self) -> usize { 15 | self.character_index 16 | } 17 | 18 | pub fn get_string(&self) -> &String { 19 | &self.buf 20 | } 21 | 22 | pub fn move_cursor_left(&mut self) { 23 | let cursor_moved_left = self.character_index.saturating_sub(1); 24 | self.character_index = self.clamp_cursor(cursor_moved_left); 25 | } 26 | 27 | pub fn move_cursor_right(&mut self) { 28 | let cursor_moved_right = self.character_index.saturating_add(1); 29 | self.character_index = self.clamp_cursor(cursor_moved_right); 30 | } 31 | 32 | pub fn enter_char(&mut self, new_char: char) { 33 | let index = self.byte_index(); 34 | self.buf.insert(index, new_char); 35 | self.move_cursor_right(); 36 | } 37 | 38 | pub fn byte_index(&mut self) -> usize { 39 | self.buf 40 | .char_indices() 41 | .map(|(i, _)| i) 42 | .nth(self.character_index) 43 | .unwrap_or(self.buf.len()) 44 | } 45 | 46 | pub fn delete_char(&mut self) { 47 | let is_not_cursor_leftmost = self.character_index != 0; 48 | if is_not_cursor_leftmost { 49 | // Method "remove" is not used on the saved text for deleting the selected char. 50 | // Reason: Using remove on String works on bytes instead of the chars. 51 | // Using remove would require special care because of char boundaries. 52 | 53 | let current_index = self.character_index; 54 | let from_left_to_current_index = current_index - 1; 55 | 56 | // Getting all characters before the selected character. 57 | let before_char_to_delete = self.buf.chars().take(from_left_to_current_index); 58 | // Getting all characters after selected character. 59 | let after_char_to_delete = self.buf.chars().skip(current_index); 60 | 61 | // Put all characters together except the selected one. 62 | // By leaving the selected one out, it is forgotten and therefore deleted. 63 | self.buf = before_char_to_delete.chain(after_char_to_delete).collect(); 64 | self.move_cursor_left(); 65 | } 66 | } 67 | 68 | pub fn clamp_cursor(&self, new_cursor_pos: usize) -> usize { 69 | new_cursor_pos.clamp(0, self.buf.chars().count()) 70 | } 71 | 72 | pub fn reset_cursor(&mut self) { 73 | self.buf.clear(); 74 | self.character_index = 0; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/ui/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | use std::{io::Read, time::Duration}; 3 | use tokio::sync::mpsc::{self, Receiver, Sender}; 4 | use index::IndexPage; 5 | use layout::MainLayout; 6 | use ratatui::{backend::Backend, crossterm::event::KeyEvent, layout::Rect, Frame, Terminal}; 7 | use tokio_serial::{DataBits, FlowControl, Parity, SerialPort, SerialPortBuilderExt, SerialStream, StopBits}; 8 | 9 | pub enum Mode { 10 | Command, 11 | Input 12 | } 13 | 14 | pub enum Page { 15 | Index, 16 | Main, 17 | Exit 18 | } 19 | 20 | pub enum Action{ 21 | Input(KeyEvent), 22 | Data(Vec) 23 | } 24 | 25 | 26 | pub mod index; 27 | pub mod layout; 28 | pub mod rxtx; 29 | 30 | pub struct AppContext{ 31 | path:String, 32 | baud_rate:u32, 33 | data_bits:DataBits, 34 | stop_bits:StopBits, 35 | parity:Parity, 36 | flow_control:FlowControl, 37 | page:Page 38 | } 39 | 40 | impl AppContext { 41 | 42 | pub const fn new()->Self{ 43 | Self { 44 | path: String::new(), 45 | baud_rate: 115200, 46 | data_bits: DataBits::Eight, 47 | stop_bits: StopBits::One, 48 | parity: Parity::None, 49 | flow_control: FlowControl::None, 50 | page:Page::Index 51 | } 52 | } 53 | 54 | async fn serial_read(&self, serial_stream: SerialStream, tx_channel: Sender){ 55 | let tx = tx_channel.clone(); 56 | let serial_rx = serial_stream; 57 | tokio::spawn(async move { 58 | if let x = serial_rx.read(buf).unwrap(){ 59 | 60 | } 61 | tx 62 | }); 63 | } 64 | 65 | pub async fn run_app(&mut self, terminal:&mut Terminal)->std::io::Result<()>{ 66 | loop{ 67 | match self.page { 68 | Page::Index => { 69 | let serial_list = tokio_serial::available_ports().expect("not found port path"); 70 | self.path = serial_list[0].port_name.clone(); 71 | self.page = IndexPage::new(serial_list).run(self, terminal) 72 | }, 73 | Page::Main => { 74 | let (event_tx, event_rx) = mpsc::channel::(64); 75 | let (send_tx, send_rx) = mpsc::channel::>(64); 76 | 77 | let mut serial = tokio_serial::new(self.path.clone(), self.baud_rate) 78 | .data_bits(self.data_bits) 79 | .stop_bits(self.stop_bits) 80 | .parity(self.parity) 81 | .flow_control(self.flow_control) 82 | .timeout(Duration::from_micros(1)) 83 | .open_native_async() 84 | .expect(format!("open {} failed!", self.path).as_str()); 85 | self.serial_read(serial, event_tx); 86 | self.page = MainLayout::default().run(terminal,&mut serial) 87 | }, 88 | Page::Exit => { 89 | return Ok(()); 90 | }, 91 | } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /src/ui/rxtx.rs: -------------------------------------------------------------------------------- 1 | 2 | use core::str; 3 | 4 | use ratatui::{ 5 | crossterm::event::{KeyCode, KeyEvent}, 6 | layout::{Constraint, Layout, Rect}, 7 | text::Line, 8 | widgets::{List, ListItem, Paragraph}, 9 | Frame, 10 | }; 11 | use tokio_serial::SerialStream; 12 | 13 | use crate::ui::Mode; 14 | use crate::common::input::Input; 15 | 16 | use super::layout::MyWidget; 17 | 18 | pub struct RxTxWidget { 19 | receive_buf: Vec, 20 | input:Input, 21 | hex_mode: bool, 22 | qa_mode: bool, 23 | enter_end:bool, 24 | } 25 | 26 | impl RxTxWidget { 27 | pub const fn new() -> Self { 28 | Self { 29 | receive_buf: vec![], 30 | input: Input::new(), 31 | hex_mode: false, 32 | qa_mode: false, 33 | enter_end:false, 34 | } 35 | } 36 | } 37 | 38 | impl MyWidget for RxTxWidget { 39 | fn event(&mut self, key: &KeyEvent) { 40 | match key.code { 41 | KeyCode::Char('h') => self.hex_mode = !self.hex_mode, 42 | KeyCode::Char('a') => self.qa_mode = !self.qa_mode, 43 | KeyCode::Char('n') => self.enter_end = !self.enter_end, 44 | _ => {} 45 | } 46 | } 47 | 48 | fn input(&mut self, key: &KeyEvent, serial:&mut SerialStream) { 49 | let mut read_buf = vec![0;4096]; 50 | if let Ok(size) = serial.try_read(read_buf.as_mut_slice()){ 51 | if let Ok(str) = str::from_utf8(&read_buf[..size].to_vec()) { 52 | self.receive_buf.push(str.to_string()) 53 | } 54 | } 55 | 56 | 57 | match key.code { 58 | KeyCode::Char(c) => self.input.enter_char(c), 59 | KeyCode::Backspace => self.input.delete_char(), 60 | KeyCode::Left => self.input.move_cursor_left(), 61 | KeyCode::Right => self.input.move_cursor_right(), 62 | KeyCode::Enter => { 63 | serial.try_write(self.input.get_string().as_bytes()).unwrap(); 64 | self.input.reset_cursor(); 65 | }, 66 | _ => {} 67 | } 68 | } 69 | 70 | fn build(&self, area: Rect, f: &mut Frame, mode: &Mode) { 71 | 72 | let [text_area, send_area] = 73 | Layout::vertical([Constraint::Fill(1), Constraint::Length(1)]).areas(area); 74 | 75 | let list = self 76 | .receive_buf 77 | .iter() 78 | .map(|v| ListItem::new(Line::from(v.as_str()))); 79 | f.render_widget(List::new(list), text_area); 80 | match mode { 81 | Mode::Command => {} 82 | Mode::Input => f.set_cursor(send_area.x + self.input.get_index() as u16 + 1, send_area.y), 83 | } 84 | f.render_widget(Paragraph::new(format!(">{}", self.input.get_string())), send_area); 85 | } 86 | 87 | fn state_list(&self) -> Vec { 88 | return vec![ 89 | format!("[{0}]Hex Mode(h)", if self.hex_mode { "x" } else { " " }), 90 | format!("[{0}]QA Mode(a)", if self.qa_mode { "x" } else { " " }), 91 | format!("[{0}]\\r\\n End(n)", if self.enter_end { "x" } else { " " }), 92 | ]; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/ui/layout.rs: -------------------------------------------------------------------------------- 1 | use ratatui::{ 2 | backend::Backend, 3 | crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}, 4 | layout::{Constraint, Layout, Rect}, 5 | text::Line, 6 | widgets::{Paragraph, Tabs}, 7 | Frame, Terminal, 8 | }; 9 | use strum::IntoEnumIterator; 10 | 11 | use strum::{Display, EnumIter, FromRepr}; 12 | use tokio_serial::SerialStream; 13 | 14 | use crate::ui::{Mode, Page}; 15 | 16 | use super::rxtx::RxTxWidget; 17 | 18 | pub trait MyWidget { 19 | fn event(&mut self, key: &KeyEvent); 20 | fn input(&mut self, key: &KeyEvent, serial: &mut SerialStream); 21 | fn build(&self, area: Rect, f: &mut Frame, mode: &Mode); 22 | fn state_list(&self) -> Vec; 23 | } 24 | 25 | #[derive(Default, Clone, Copy, Display, FromRepr, EnumIter)] 26 | pub enum SelectedTab { 27 | #[default] 28 | #[strum(to_string = "TxRx(t)")] 29 | TxRx, 30 | #[strum(to_string = "List(l)")] 31 | Command, 32 | #[strum(to_string = "Stream(s)")] 33 | Stream, 34 | #[strum(to_string = "Ymodem(y)")] 35 | Ymodem, 36 | #[strum(to_string = "Chart(c)")] 37 | Chart, 38 | } 39 | 40 | impl SelectedTab { 41 | fn title(self) -> Line<'static> { 42 | format!(" {self} ").into() 43 | } 44 | 45 | fn previous(self) -> Self { 46 | let current_index = self as usize; 47 | if current_index == 0 { 48 | return SelectedTab::Chart; 49 | } 50 | let previous_index = current_index.saturating_sub(1); 51 | Self::from_repr(previous_index).unwrap() 52 | } 53 | 54 | fn next(self) -> Self { 55 | let current_index = self as usize; 56 | let next_index = current_index.saturating_add(1); 57 | Self::from_repr(next_index).unwrap_or(SelectedTab::TxRx) 58 | } 59 | } 60 | 61 | pub struct MainLayout { 62 | send_count: usize, 63 | receive_count: usize, 64 | selected_tab: SelectedTab, 65 | mode: Mode, 66 | 67 | widget: Box, 68 | } 69 | 70 | impl Default for MainLayout { 71 | fn default() -> Self { 72 | Self { 73 | send_count: Default::default(), 74 | receive_count: Default::default(), 75 | selected_tab: Default::default(), 76 | mode: Mode::Command, 77 | widget: Box::new(RxTxWidget::new()), 78 | } 79 | } 80 | } 81 | 82 | impl MainLayout { 83 | pub fn run( 84 | &mut self, 85 | terminal: &mut Terminal, 86 | serial: &mut SerialStream, 87 | ) -> Page { 88 | loop { 89 | self.draw(terminal, serial); 90 | if let Some(page) = self.event(serial) { 91 | return page; 92 | } 93 | } 94 | } 95 | 96 | fn draw(&self, terminal: &mut Terminal, serial: &mut SerialStream) { 97 | terminal.draw(|f| self.build(f, serial)).unwrap(); 98 | } 99 | 100 | fn event(&mut self, serial: &mut SerialStream) -> Option { 101 | if let Ok(Event::Key(key)) = event::read() { 102 | if key.kind == KeyEventKind::Press { 103 | match self.mode { 104 | Mode::Command => match key.code { 105 | KeyCode::Esc => return Some(Page::Index), 106 | KeyCode::Char('q') => return Some(Page::Exit), 107 | KeyCode::Char('t') => self.selected_tab = SelectedTab::TxRx, 108 | KeyCode::Char('l') => self.selected_tab = SelectedTab::Command, 109 | KeyCode::Char('s') => self.selected_tab = SelectedTab::Stream, 110 | KeyCode::Char('y') => self.selected_tab = SelectedTab::Ymodem, 111 | KeyCode::Char('c') => self.selected_tab = SelectedTab::Chart, 112 | KeyCode::Char('i') => self.mode = Mode::Input, 113 | KeyCode::Right => self.selected_tab = self.selected_tab.next(), 114 | KeyCode::Left => self.selected_tab = self.selected_tab.previous(), 115 | _ => self.widget.event(&key), 116 | }, 117 | Mode::Input => match key.code { 118 | KeyCode::Esc => self.mode = Mode::Command, 119 | _ => self.widget.input(&key, serial), 120 | }, 121 | } 122 | } 123 | } 124 | None 125 | } 126 | 127 | fn read(&self, serial: &mut SerialStream) -> Option> { 128 | let mut read_buf = vec![0; 4096]; 129 | if let Ok(size) = serial.try_read(read_buf.as_mut_slice()) { 130 | return Some(read_buf[..size].to_vec()); 131 | } 132 | return None; 133 | } 134 | 135 | fn build(&self, f: &mut Frame, serial: &mut SerialStream) { 136 | let layout = Layout::vertical([ 137 | Constraint::Length(1), 138 | Constraint::Fill(1), 139 | Constraint::Length(1), 140 | ]) 141 | .split(f.size()); 142 | 143 | let [tab_area, text_area] = 144 | Layout::horizontal([Constraint::Percentage(80), Constraint::Percentage(20)]) 145 | .areas(layout[0]); 146 | 147 | let state_layout = Layout::horizontal([ 148 | Constraint::Percentage(10), 149 | Constraint::Percentage(10), 150 | Constraint::Percentage(10), 151 | Constraint::Percentage(10), 152 | Constraint::Percentage(10), 153 | Constraint::Percentage(10), 154 | Constraint::Percentage(10), 155 | Constraint::Percentage(10), 156 | Constraint::Percentage(10), 157 | Constraint::Percentage(10), 158 | ]) 159 | .split(layout[2]); 160 | 161 | let titles = SelectedTab::iter().map(SelectedTab::title); 162 | 163 | let mut state_tabs = vec![ 164 | String::from(format!("send:{0}", self.send_count)), 165 | String::from(format!("receive:{0}", self.receive_count)), 166 | ]; 167 | 168 | for v in self.widget.state_list().iter() { 169 | state_tabs.push(v.clone()); 170 | } 171 | 172 | f.render_widget( 173 | Tabs::new(titles).select(self.selected_tab as usize), 174 | tab_area, 175 | ); 176 | f.render_widget( 177 | Paragraph::new("[i] input mode | [q] exit app | [Esc] back"), 178 | text_area, 179 | ); 180 | self.widget.build(layout[1], f, &self.mode); 181 | for (i, v) in state_tabs.iter().enumerate() { 182 | f.render_widget(Paragraph::new(v.clone()), state_layout[i]); 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/ui/demo.rs: -------------------------------------------------------------------------------- 1 | 2 | use ratatui::{ 3 | crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind}, 4 | prelude::*, 5 | widgets::{Block, List, ListItem, Paragraph}, 6 | }; 7 | 8 | use super::{Page, PageEnum, AppContext}; 9 | 10 | enum InputMode { 11 | Normal, 12 | Editing, 13 | } 14 | 15 | pub struct App { 16 | /// Current value of the input box 17 | input: String, 18 | /// Position of cursor in the editor area. 19 | character_index: usize, 20 | /// Current input mode 21 | input_mode: InputMode, 22 | /// History of recorded messages 23 | messages: Vec, 24 | } 25 | impl App { 26 | pub const fn new() -> Self { 27 | Self { 28 | input: String::new(), 29 | input_mode: InputMode::Normal, 30 | messages: Vec::new(), 31 | character_index: 0, 32 | } 33 | } 34 | 35 | fn move_cursor_left(&mut self) { 36 | let cursor_moved_left = self.character_index.saturating_sub(1); 37 | self.character_index = self.clamp_cursor(cursor_moved_left); 38 | } 39 | 40 | fn move_cursor_right(&mut self) { 41 | let cursor_moved_right = self.character_index.saturating_add(1); 42 | self.character_index = self.clamp_cursor(cursor_moved_right); 43 | } 44 | 45 | fn enter_char(&mut self, new_char: char) { 46 | let index = self.byte_index(); 47 | self.input.insert(index, new_char); 48 | self.move_cursor_right(); 49 | } 50 | 51 | /// Returns the byte index based on the character position. 52 | /// 53 | /// Since each character in a string can be contain multiple bytes, it's necessary to calculate 54 | /// the byte index based on the index of the character. 55 | fn byte_index(&mut self) -> usize { 56 | self.input 57 | .char_indices() 58 | .map(|(i, _)| i) 59 | .nth(self.character_index) 60 | .unwrap_or(self.input.len()) 61 | } 62 | 63 | fn delete_char(&mut self) { 64 | let is_not_cursor_leftmost = self.character_index != 0; 65 | if is_not_cursor_leftmost { 66 | // Method "remove" is not used on the saved text for deleting the selected char. 67 | // Reason: Using remove on String works on bytes instead of the chars. 68 | // Using remove would require special care because of char boundaries. 69 | 70 | let current_index = self.character_index; 71 | let from_left_to_current_index = current_index - 1; 72 | 73 | // Getting all characters before the selected character. 74 | let before_char_to_delete = self.input.chars().take(from_left_to_current_index); 75 | // Getting all characters after selected character. 76 | let after_char_to_delete = self.input.chars().skip(current_index); 77 | 78 | // Put all characters together except the selected one. 79 | // By leaving the selected one out, it is forgotten and therefore deleted. 80 | self.input = before_char_to_delete.chain(after_char_to_delete).collect(); 81 | self.move_cursor_left(); 82 | } 83 | } 84 | 85 | fn clamp_cursor(&self, new_cursor_pos: usize) -> usize { 86 | new_cursor_pos.clamp(0, self.input.chars().count()) 87 | } 88 | 89 | fn reset_cursor(&mut self) { 90 | self.character_index = 0; 91 | } 92 | 93 | fn submit_message(&mut self) { 94 | self.messages.push(self.input.clone()); 95 | self.input.clear(); 96 | self.reset_cursor(); 97 | } 98 | } 99 | 100 | impl Page for App { 101 | fn event(&mut self, _context: &mut AppContext, key: &KeyEvent) -> Option { 102 | match self.input_mode { 103 | InputMode::Normal => match key.code { 104 | KeyCode::Char('e') => { 105 | self.input_mode = InputMode::Editing 106 | } 107 | KeyCode::Char('q') => { 108 | return Some(PageEnum::Exit) 109 | } 110 | _ => {} 111 | }, 112 | InputMode::Editing if key.kind == KeyEventKind::Press => match key.code { 113 | KeyCode::Enter => self.submit_message(), 114 | KeyCode::Char(to_insert) => { 115 | self.enter_char(to_insert); 116 | } 117 | KeyCode::Backspace => { 118 | self.delete_char(); 119 | } 120 | KeyCode::Left => { 121 | self.move_cursor_left(); 122 | } 123 | KeyCode::Right => { 124 | self.move_cursor_right(); 125 | } 126 | KeyCode::Esc => { 127 | self.input_mode = InputMode::Normal; 128 | } 129 | _ => {} 130 | }, 131 | InputMode::Editing => {} 132 | } 133 | return None 134 | } 135 | 136 | fn render(&self, _context: &mut AppContext, f: &mut Frame) { 137 | let vertical = Layout::vertical([ 138 | Constraint::Length(1), 139 | Constraint::Length(3), 140 | Constraint::Min(1), 141 | ]); 142 | let [help_area, input_area, messages_area] = vertical.areas(f.size()); 143 | 144 | let (msg, style) = match self.input_mode { 145 | InputMode::Normal => ( 146 | vec![ 147 | "Press ".into(), 148 | "q".bold(), 149 | " to exit, ".into(), 150 | "e".bold(), 151 | " to start editing.".bold(), 152 | ], 153 | Style::default().add_modifier(Modifier::RAPID_BLINK), 154 | ), 155 | InputMode::Editing => ( 156 | vec![ 157 | "Press ".into(), 158 | "Esc".bold(), 159 | " to stop editing, ".into(), 160 | "Enter".bold(), 161 | " to record the message".into(), 162 | ], 163 | Style::default(), 164 | ), 165 | }; 166 | let text = Text::from(Line::from(msg)).patch_style(style); 167 | let help_message = Paragraph::new(text); 168 | f.render_widget(help_message, help_area); 169 | 170 | let input = Paragraph::new(self.input.as_str()) 171 | .style(match self.input_mode { 172 | InputMode::Normal => Style::default(), 173 | InputMode::Editing => Style::default().fg(Color::Yellow), 174 | }) 175 | .block(Block::bordered().title("Input")); 176 | f.render_widget(input, input_area); 177 | match self.input_mode { 178 | InputMode::Normal => 179 | // Hide the cursor. `Frame` does this by default, so we don't need to do anything here 180 | {} 181 | 182 | InputMode::Editing => { 183 | // Make the cursor visible and ask ratatui to put it at the specified coordinates after 184 | // rendering 185 | #[allow(clippy::cast_possible_truncation)] 186 | f.set_cursor( 187 | // Draw the cursor at the current position in the input field. 188 | // This position is can be controlled via the left and right arrow key 189 | input_area.x + self.character_index as u16 + 1, 190 | // Move one line down, from the border to the input line 191 | input_area.y + 1, 192 | ); 193 | } 194 | } 195 | 196 | let messages: Vec = self 197 | .messages 198 | .iter() 199 | .enumerate() 200 | .map(|(i, m)| { 201 | let content = Line::from(Span::raw(format!("{i}: {m}"))); 202 | ListItem::new(content) 203 | }) 204 | .collect(); 205 | let messages = List::new(messages).block(Block::bordered().title("Messages")); 206 | f.render_widget(messages, messages_area); 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/ui/index.rs: -------------------------------------------------------------------------------- 1 | use indoc::indoc; 2 | use ratatui::{ 3 | backend::Backend, 4 | crossterm::{ 5 | event::{self, Event, KeyCode, KeyEventKind}, 6 | style::Color, 7 | }, 8 | layout::{Constraint, Layout}, 9 | style::Stylize, 10 | text::Line, 11 | widgets::Paragraph, 12 | Frame, Terminal, 13 | }; 14 | use strum::{Display, EnumIter, FromRepr, IntoEnumIterator}; 15 | use tokio_serial::{DataBits, FlowControl, Parity, SerialPortInfo, StopBits}; 16 | 17 | use crate::ui::{AppContext, Page}; 18 | 19 | #[derive(PartialEq, Clone, Copy, Display, FromRepr, EnumIter)] 20 | enum Menu { 21 | #[strum(to_string = "Serial Port")] 22 | SerialPort, 23 | #[strum(to_string = "Baud Rate")] 24 | BaudRate, 25 | #[strum(to_string = "Data Bits")] 26 | DataBits, 27 | #[strum(to_string = "Stop Bits")] 28 | StopBits, 29 | #[strum(to_string = "Parity")] 30 | Parity, 31 | #[strum(to_string = "Flow Conntrol")] 32 | FlowConntrol, 33 | } 34 | 35 | impl Menu { 36 | fn previous(self) -> Self { 37 | let current_index = self as usize; 38 | if current_index == 0 { 39 | return Menu::FlowConntrol; 40 | } 41 | let previous_index = current_index.saturating_sub(1); 42 | Self::from_repr(previous_index).unwrap() 43 | } 44 | 45 | fn next(self) -> Self { 46 | let current_index = self as usize; 47 | let next_index = current_index.saturating_add(1); 48 | Self::from_repr(next_index).unwrap_or(Menu::SerialPort) 49 | } 50 | } 51 | 52 | fn logo() -> String { 53 | let str = indoc! {r" 54 | ______ ______ ______ __ ______ __ ______ ______ ______ __ 55 | /\ ___\ /\ ___\ /\ == \ /\ \ /\ __ \ /\ \ /\__ _\ /\ __ \ /\ __ \ /\ \ 56 | \ \___ \ \ \ __\ \ \ __< \ \ \ \ \ __ \ \ \ \____ \/_/\ \/ \ \ \/\ \ \ \ \/\ \ \ \ \____ 57 | \/\_____\ \ \_____\ \ \_\ \_\ \ \_\ \ \_\ \_\ \ \_____\ \ \_\ \ \_____\ \ \_____\ \ \_____\ 58 | \/_____/ \/_____/ \/_/ /_/ \/_/ \/_/\/_/ \/_____/ \/_/ \/_____/ \/_____/ \/_____/ 59 | "}; 60 | return String::from(str); 61 | } 62 | 63 | const BAUD_RATE: [u32; 20] = [ 64 | 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 256000, 65 | 460800, 512000, 750000, 900000, 921600, 1500000, 66 | ]; 67 | 68 | const DATA_BITS: [DataBits; 4] = [ 69 | DataBits::Five, 70 | DataBits::Six, 71 | DataBits::Seven, 72 | DataBits::Eight, 73 | ]; 74 | 75 | const STOP_BITS: [StopBits; 2] = [StopBits::One, StopBits::Two]; 76 | 77 | const PARITY: [Parity; 3] = [Parity::None, Parity::Even, Parity::Odd]; 78 | 79 | const FLOW: [FlowControl; 3] = [ 80 | FlowControl::None, 81 | FlowControl::Software, 82 | FlowControl::Hardware, 83 | ]; 84 | 85 | pub struct IndexPage { 86 | position: Menu, 87 | select: bool, 88 | index: usize, 89 | port_list: Vec, 90 | } 91 | 92 | impl IndexPage { 93 | pub const fn new(info: Vec) -> Self { 94 | return Self { 95 | position: Menu::SerialPort, 96 | select: false, 97 | index: 0, 98 | port_list: info, 99 | }; 100 | } 101 | 102 | fn title(&self, position: Menu, value: &String) -> String { 103 | let mut text = if self.position == position && self.select { 104 | String::from("<") 105 | } else if self.position == position { 106 | String::from(">") 107 | } else { 108 | String::from(" ") 109 | }; 110 | text.push_str(&position.to_string()); 111 | text.push_str(": "); 112 | text.push_str(value.as_str()); 113 | return text; 114 | } 115 | 116 | fn add_item(&self, text: &mut Vec, key: usize, value: &String) { 117 | let mut str = String::new(); 118 | str.push_str(" "); 119 | str.push_str(key.to_string().as_str()); 120 | str.push_str(": "); 121 | str.push_str(value.as_str()); 122 | 123 | text.push(if self.index == key { 124 | Line::from(str).fg(Color::Yellow) 125 | } else { 126 | Line::from(str) 127 | }); 128 | } 129 | 130 | fn add_number(&mut self, n: usize) { 131 | if self.index >= 10 { 132 | return; 133 | } 134 | self.index *= 10; 135 | self.index += n; 136 | } 137 | 138 | fn delete_number(&mut self) { 139 | self.index /= 10 140 | } 141 | 142 | fn down(&mut self) { 143 | if self.select { 144 | self.index += 1; 145 | } else { 146 | self.position = self.position.next(); 147 | } 148 | } 149 | 150 | fn up(&mut self) { 151 | if self.select{ 152 | if self.index != 0{ 153 | self.index -= 1; 154 | } 155 | } 156 | else{ 157 | self.position = self.position.previous(); 158 | } 159 | } 160 | 161 | pub fn run( 162 | &mut self, 163 | context: &mut AppContext, 164 | terminal: &mut Terminal, 165 | ) -> Page { 166 | context.path = self.port_list[0].port_name.clone(); 167 | loop { 168 | self.draw(context, terminal); 169 | if let Some(p) = self.event(context) { 170 | return p; 171 | } 172 | } 173 | } 174 | 175 | fn draw(&self, context: &AppContext, terminal: &mut Terminal) { 176 | terminal.draw(|f| self.build(context, f)).unwrap(); 177 | } 178 | 179 | fn event(&mut self, context: &mut AppContext) -> Option { 180 | if let Ok(Event::Key(key)) = event::read() { 181 | if key.kind == KeyEventKind::Press { 182 | match key.code { 183 | KeyCode::Enter => return Some(Page::Main), 184 | KeyCode::Char('q') => return Some(Page::Exit), 185 | KeyCode::Down => self.down(), 186 | KeyCode::Up => self.up(), 187 | KeyCode::Char('0') => { 188 | if self.select { 189 | self.add_number(0) 190 | } 191 | } 192 | KeyCode::Char('1') => { 193 | if self.select { 194 | self.add_number(1) 195 | } 196 | } 197 | KeyCode::Char('2') => { 198 | if self.select { 199 | self.add_number(2) 200 | } 201 | } 202 | KeyCode::Char('3') => { 203 | if self.select { 204 | self.add_number(3) 205 | } 206 | } 207 | KeyCode::Char('4') => { 208 | if self.select { 209 | self.add_number(4) 210 | } 211 | } 212 | KeyCode::Char('5') => { 213 | if self.select { 214 | self.add_number(5) 215 | } 216 | } 217 | KeyCode::Char('6') => { 218 | if self.select { 219 | self.add_number(6) 220 | } 221 | } 222 | KeyCode::Char('7') => { 223 | if self.select { 224 | self.add_number(7) 225 | } 226 | } 227 | KeyCode::Char('8') => { 228 | if self.select { 229 | self.add_number(8) 230 | } 231 | } 232 | KeyCode::Char('9') => { 233 | if self.select { 234 | self.add_number(9) 235 | } 236 | } 237 | KeyCode::Backspace => { 238 | if self.select { 239 | self.delete_number() 240 | } 241 | } 242 | KeyCode::Right => self.select = true, 243 | KeyCode::Left => { 244 | self.select = false; 245 | match self.position { 246 | Menu::SerialPort => { 247 | if self.index < self.port_list.len() { 248 | context.path = self.port_list[self.index].port_name.clone() 249 | } 250 | } 251 | 252 | Menu::BaudRate => { 253 | if self.index < BAUD_RATE.len() { 254 | context.baud_rate = BAUD_RATE[self.index] 255 | } 256 | } 257 | 258 | Menu::DataBits => { 259 | if self.index < DATA_BITS.len() { 260 | context.data_bits = DATA_BITS[self.index] 261 | } 262 | } 263 | 264 | Menu::StopBits => { 265 | if self.index < STOP_BITS.len() { 266 | context.stop_bits = STOP_BITS[self.index] 267 | } 268 | } 269 | 270 | Menu::Parity => { 271 | if self.index < PARITY.len() { 272 | context.parity = PARITY[self.index] 273 | } 274 | } 275 | 276 | Menu::FlowConntrol => { 277 | if self.index < FLOW.len() { 278 | context.flow_control = FLOW[self.index] 279 | } 280 | } 281 | } 282 | self.index = 0; 283 | } 284 | _ => {} 285 | } 286 | } 287 | } 288 | None 289 | } 290 | 291 | fn build(&self, context: &AppContext, f: &mut Frame) { 292 | let layout = Layout::vertical([ 293 | Constraint::Percentage(10), 294 | Constraint::Percentage(90), 295 | // Constraint::Percentage(60), 296 | ]) 297 | .split(f.size()); 298 | let menu_layout = 299 | Layout::horizontal([Constraint::Percentage(33), Constraint::Percentage(67)]) 300 | .split(layout[1]); 301 | 302 | let mut line_list = Vec::::new(); 303 | let logo = logo(); 304 | for v in logo.split('\n') { 305 | line_list.push(Line::from(v)); 306 | } 307 | line_list.push(Line::from("-by ThinkCode").fg(Color::White)); 308 | line_list.push(Line::from("")); 309 | line_list.push( 310 | Line::from( 311 | "*Press [Up&Down] to move, Press [Right] into item, Press [Left] back and save", 312 | ) 313 | .fg(Color::White), 314 | ); 315 | line_list.push(Line::from("*Press [Enter] to open Serial").fg(Color::Yellow)); 316 | line_list.push(Line::from("*Press [q] to exit app").fg(Color::Red)); 317 | line_list.push(Line::from("")); 318 | 319 | for menu in Menu::iter() { 320 | match menu { 321 | Menu::SerialPort => { 322 | line_list.push(Line::from(self.title(menu, &context.path))); 323 | if self.position == menu && self.select { 324 | for (i, v) in self.port_list.iter().enumerate() { 325 | self.add_item(&mut line_list, i, &v.port_name) 326 | } 327 | } 328 | } 329 | Menu::BaudRate => { 330 | line_list.push(Line::from(self.title(menu, &context.baud_rate.to_string()))); 331 | if self.position == menu && self.select { 332 | for (i, v) in BAUD_RATE.iter().enumerate() { 333 | self.add_item(&mut line_list, i, &v.to_string()) 334 | } 335 | } 336 | } 337 | Menu::DataBits => { 338 | line_list.push(Line::from(self.title(menu, &context.data_bits.to_string()))); 339 | if self.position == menu && self.select { 340 | for (i, v) in DATA_BITS.iter().enumerate() { 341 | self.add_item(&mut line_list, i, &v.to_string()) 342 | } 343 | } 344 | } 345 | Menu::StopBits => { 346 | line_list.push(Line::from(self.title(menu, &context.stop_bits.to_string()))); 347 | if self.position == menu && self.select { 348 | for (i, v) in STOP_BITS.iter().enumerate() { 349 | self.add_item(&mut line_list, i, &v.to_string()) 350 | } 351 | } 352 | } 353 | Menu::Parity => { 354 | line_list.push(Line::from(self.title(menu, &context.parity.to_string()))); 355 | if self.position == menu && self.select { 356 | for (i, v) in PARITY.iter().enumerate() { 357 | self.add_item(&mut line_list, i, &v.to_string()) 358 | } 359 | } 360 | } 361 | Menu::FlowConntrol => { 362 | line_list.push(Line::from( 363 | self.title(menu, &context.flow_control.to_string()), 364 | )); 365 | if self.position == menu && self.select { 366 | for (i, v) in FLOW.iter().enumerate() { 367 | self.add_item(&mut line_list, i, &v.to_string()) 368 | } 369 | } 370 | } 371 | } 372 | } 373 | 374 | // f.render_widget(Paragraph::new(logo()).centered(), layout[1]); 375 | let paragraph = Paragraph::new(line_list).left_aligned(); 376 | f.render_widget(paragraph, menu_layout[1]); 377 | } 378 | } 379 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.22.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "once_cell", 28 | "version_check", 29 | "zerocopy", 30 | ] 31 | 32 | [[package]] 33 | name = "aho-corasick" 34 | version = "1.1.3" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 37 | dependencies = [ 38 | "memchr", 39 | ] 40 | 41 | [[package]] 42 | name = "allocator-api2" 43 | version = "0.2.18" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 46 | 47 | [[package]] 48 | name = "autocfg" 49 | version = "1.3.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 52 | 53 | [[package]] 54 | name = "backtrace" 55 | version = "0.3.73" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" 58 | dependencies = [ 59 | "addr2line", 60 | "cc", 61 | "cfg-if", 62 | "libc", 63 | "miniz_oxide", 64 | "object", 65 | "rustc-demangle", 66 | ] 67 | 68 | [[package]] 69 | name = "bitflags" 70 | version = "1.3.2" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 73 | 74 | [[package]] 75 | name = "bitflags" 76 | version = "2.6.0" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 79 | 80 | [[package]] 81 | name = "bytes" 82 | version = "1.6.1" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" 85 | 86 | [[package]] 87 | name = "cassowary" 88 | version = "0.3.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 91 | 92 | [[package]] 93 | name = "castaway" 94 | version = "0.2.3" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" 97 | dependencies = [ 98 | "rustversion", 99 | ] 100 | 101 | [[package]] 102 | name = "cc" 103 | version = "1.1.6" 104 | source = "registry+https://github.com/rust-lang/crates.io-index" 105 | checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" 106 | 107 | [[package]] 108 | name = "cfg-if" 109 | version = "1.0.0" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 112 | 113 | [[package]] 114 | name = "compact_str" 115 | version = "0.7.1" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" 118 | dependencies = [ 119 | "castaway", 120 | "cfg-if", 121 | "itoa", 122 | "ryu", 123 | "static_assertions", 124 | ] 125 | 126 | [[package]] 127 | name = "core-foundation-sys" 128 | version = "0.8.6" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" 131 | 132 | [[package]] 133 | name = "crossterm" 134 | version = "0.27.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" 137 | dependencies = [ 138 | "bitflags 2.6.0", 139 | "crossterm_winapi", 140 | "libc", 141 | "mio 0.8.11", 142 | "parking_lot", 143 | "signal-hook", 144 | "signal-hook-mio", 145 | "winapi", 146 | ] 147 | 148 | [[package]] 149 | name = "crossterm_winapi" 150 | version = "0.9.1" 151 | source = "registry+https://github.com/rust-lang/crates.io-index" 152 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 153 | dependencies = [ 154 | "winapi", 155 | ] 156 | 157 | [[package]] 158 | name = "either" 159 | version = "1.13.0" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 162 | 163 | [[package]] 164 | name = "futures" 165 | version = "0.3.30" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" 168 | dependencies = [ 169 | "futures-channel", 170 | "futures-core", 171 | "futures-executor", 172 | "futures-io", 173 | "futures-sink", 174 | "futures-task", 175 | "futures-util", 176 | ] 177 | 178 | [[package]] 179 | name = "futures-channel" 180 | version = "0.3.30" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 183 | dependencies = [ 184 | "futures-core", 185 | "futures-sink", 186 | ] 187 | 188 | [[package]] 189 | name = "futures-core" 190 | version = "0.3.30" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 193 | 194 | [[package]] 195 | name = "futures-executor" 196 | version = "0.3.30" 197 | source = "registry+https://github.com/rust-lang/crates.io-index" 198 | checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" 199 | dependencies = [ 200 | "futures-core", 201 | "futures-task", 202 | "futures-util", 203 | ] 204 | 205 | [[package]] 206 | name = "futures-io" 207 | version = "0.3.30" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 210 | 211 | [[package]] 212 | name = "futures-macro" 213 | version = "0.3.30" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" 216 | dependencies = [ 217 | "proc-macro2", 218 | "quote", 219 | "syn", 220 | ] 221 | 222 | [[package]] 223 | name = "futures-sink" 224 | version = "0.3.30" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 227 | 228 | [[package]] 229 | name = "futures-task" 230 | version = "0.3.30" 231 | source = "registry+https://github.com/rust-lang/crates.io-index" 232 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 233 | 234 | [[package]] 235 | name = "futures-util" 236 | version = "0.3.30" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 239 | dependencies = [ 240 | "futures-channel", 241 | "futures-core", 242 | "futures-io", 243 | "futures-macro", 244 | "futures-sink", 245 | "futures-task", 246 | "memchr", 247 | "pin-project-lite", 248 | "pin-utils", 249 | "slab", 250 | ] 251 | 252 | [[package]] 253 | name = "gimli" 254 | version = "0.29.0" 255 | source = "registry+https://github.com/rust-lang/crates.io-index" 256 | checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" 257 | 258 | [[package]] 259 | name = "hashbrown" 260 | version = "0.14.5" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 263 | dependencies = [ 264 | "ahash", 265 | "allocator-api2", 266 | ] 267 | 268 | [[package]] 269 | name = "heck" 270 | version = "0.5.0" 271 | source = "registry+https://github.com/rust-lang/crates.io-index" 272 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 273 | 274 | [[package]] 275 | name = "hermit-abi" 276 | version = "0.3.9" 277 | source = "registry+https://github.com/rust-lang/crates.io-index" 278 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 279 | 280 | [[package]] 281 | name = "indoc" 282 | version = "2.0.5" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" 285 | 286 | [[package]] 287 | name = "io-kit-sys" 288 | version = "0.4.1" 289 | source = "registry+https://github.com/rust-lang/crates.io-index" 290 | checksum = "617ee6cf8e3f66f3b4ea67a4058564628cde41901316e19f559e14c7c72c5e7b" 291 | dependencies = [ 292 | "core-foundation-sys", 293 | "mach2", 294 | ] 295 | 296 | [[package]] 297 | name = "itertools" 298 | version = "0.13.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 301 | dependencies = [ 302 | "either", 303 | ] 304 | 305 | [[package]] 306 | name = "itoa" 307 | version = "1.0.11" 308 | source = "registry+https://github.com/rust-lang/crates.io-index" 309 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 310 | 311 | [[package]] 312 | name = "libc" 313 | version = "0.2.155" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 316 | 317 | [[package]] 318 | name = "lock_api" 319 | version = "0.4.12" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 322 | dependencies = [ 323 | "autocfg", 324 | "scopeguard", 325 | ] 326 | 327 | [[package]] 328 | name = "log" 329 | version = "0.4.22" 330 | source = "registry+https://github.com/rust-lang/crates.io-index" 331 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 332 | 333 | [[package]] 334 | name = "lru" 335 | version = "0.12.3" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" 338 | dependencies = [ 339 | "hashbrown", 340 | ] 341 | 342 | [[package]] 343 | name = "mach2" 344 | version = "0.4.2" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" 347 | dependencies = [ 348 | "libc", 349 | ] 350 | 351 | [[package]] 352 | name = "memchr" 353 | version = "2.7.4" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 356 | 357 | [[package]] 358 | name = "memoffset" 359 | version = "0.7.1" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" 362 | dependencies = [ 363 | "autocfg", 364 | ] 365 | 366 | [[package]] 367 | name = "miniz_oxide" 368 | version = "0.7.4" 369 | source = "registry+https://github.com/rust-lang/crates.io-index" 370 | checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" 371 | dependencies = [ 372 | "adler", 373 | ] 374 | 375 | [[package]] 376 | name = "mio" 377 | version = "0.8.11" 378 | source = "registry+https://github.com/rust-lang/crates.io-index" 379 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 380 | dependencies = [ 381 | "libc", 382 | "log", 383 | "wasi", 384 | "windows-sys 0.48.0", 385 | ] 386 | 387 | [[package]] 388 | name = "mio" 389 | version = "1.0.1" 390 | source = "registry+https://github.com/rust-lang/crates.io-index" 391 | checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" 392 | dependencies = [ 393 | "hermit-abi", 394 | "libc", 395 | "wasi", 396 | "windows-sys 0.52.0", 397 | ] 398 | 399 | [[package]] 400 | name = "mio-serial" 401 | version = "5.0.5" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "20a4c60ca5c9c0e114b3bd66ff4aa5f9b2b175442be51ca6c4365d687a97a2ac" 404 | dependencies = [ 405 | "log", 406 | "mio 0.8.11", 407 | "nix", 408 | "serialport", 409 | "winapi", 410 | ] 411 | 412 | [[package]] 413 | name = "nix" 414 | version = "0.26.4" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" 417 | dependencies = [ 418 | "bitflags 1.3.2", 419 | "cfg-if", 420 | "libc", 421 | "memoffset", 422 | "pin-utils", 423 | ] 424 | 425 | [[package]] 426 | name = "object" 427 | version = "0.36.2" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "3f203fa8daa7bb185f760ae12bd8e097f63d17041dcdcaf675ac54cdf863170e" 430 | dependencies = [ 431 | "memchr", 432 | ] 433 | 434 | [[package]] 435 | name = "once_cell" 436 | version = "1.19.0" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 439 | 440 | [[package]] 441 | name = "parking_lot" 442 | version = "0.12.3" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 445 | dependencies = [ 446 | "lock_api", 447 | "parking_lot_core", 448 | ] 449 | 450 | [[package]] 451 | name = "parking_lot_core" 452 | version = "0.9.10" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 455 | dependencies = [ 456 | "cfg-if", 457 | "libc", 458 | "redox_syscall", 459 | "smallvec", 460 | "windows-targets 0.52.6", 461 | ] 462 | 463 | [[package]] 464 | name = "paste" 465 | version = "1.0.15" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 468 | 469 | [[package]] 470 | name = "pin-project-lite" 471 | version = "0.2.14" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 474 | 475 | [[package]] 476 | name = "pin-utils" 477 | version = "0.1.0" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 480 | 481 | [[package]] 482 | name = "proc-macro2" 483 | version = "1.0.86" 484 | source = "registry+https://github.com/rust-lang/crates.io-index" 485 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 486 | dependencies = [ 487 | "unicode-ident", 488 | ] 489 | 490 | [[package]] 491 | name = "quote" 492 | version = "1.0.36" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 495 | dependencies = [ 496 | "proc-macro2", 497 | ] 498 | 499 | [[package]] 500 | name = "ratatui" 501 | version = "0.27.0" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "d16546c5b5962abf8ce6e2881e722b4e0ae3b6f1a08a26ae3573c55853ca68d3" 504 | dependencies = [ 505 | "bitflags 2.6.0", 506 | "cassowary", 507 | "compact_str", 508 | "crossterm", 509 | "itertools", 510 | "lru", 511 | "paste", 512 | "stability", 513 | "strum", 514 | "strum_macros", 515 | "unicode-segmentation", 516 | "unicode-truncate", 517 | "unicode-width", 518 | ] 519 | 520 | [[package]] 521 | name = "redox_syscall" 522 | version = "0.5.3" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" 525 | dependencies = [ 526 | "bitflags 2.6.0", 527 | ] 528 | 529 | [[package]] 530 | name = "regex" 531 | version = "1.10.5" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" 534 | dependencies = [ 535 | "aho-corasick", 536 | "memchr", 537 | "regex-automata", 538 | "regex-syntax", 539 | ] 540 | 541 | [[package]] 542 | name = "regex-automata" 543 | version = "0.4.7" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" 546 | dependencies = [ 547 | "aho-corasick", 548 | "memchr", 549 | "regex-syntax", 550 | ] 551 | 552 | [[package]] 553 | name = "regex-syntax" 554 | version = "0.8.4" 555 | source = "registry+https://github.com/rust-lang/crates.io-index" 556 | checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" 557 | 558 | [[package]] 559 | name = "rustc-demangle" 560 | version = "0.1.24" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 563 | 564 | [[package]] 565 | name = "rustversion" 566 | version = "1.0.17" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 569 | 570 | [[package]] 571 | name = "ryu" 572 | version = "1.0.18" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 575 | 576 | [[package]] 577 | name = "scopeguard" 578 | version = "1.2.0" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 581 | 582 | [[package]] 583 | name = "serial_tool" 584 | version = "0.1.0" 585 | dependencies = [ 586 | "indoc", 587 | "ratatui", 588 | "strum", 589 | "tokio", 590 | "tokio-serial", 591 | ] 592 | 593 | [[package]] 594 | name = "serialport" 595 | version = "4.4.0" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "de7c4f0cce25b9b3518eea99618112f9ee4549f974480c8f43d3c06f03c131a0" 598 | dependencies = [ 599 | "bitflags 2.6.0", 600 | "cfg-if", 601 | "core-foundation-sys", 602 | "io-kit-sys", 603 | "mach2", 604 | "nix", 605 | "regex", 606 | "scopeguard", 607 | "unescaper", 608 | "winapi", 609 | ] 610 | 611 | [[package]] 612 | name = "signal-hook" 613 | version = "0.3.17" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" 616 | dependencies = [ 617 | "libc", 618 | "signal-hook-registry", 619 | ] 620 | 621 | [[package]] 622 | name = "signal-hook-mio" 623 | version = "0.2.3" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" 626 | dependencies = [ 627 | "libc", 628 | "mio 0.8.11", 629 | "signal-hook", 630 | ] 631 | 632 | [[package]] 633 | name = "signal-hook-registry" 634 | version = "1.4.2" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 637 | dependencies = [ 638 | "libc", 639 | ] 640 | 641 | [[package]] 642 | name = "slab" 643 | version = "0.4.9" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 646 | dependencies = [ 647 | "autocfg", 648 | ] 649 | 650 | [[package]] 651 | name = "smallvec" 652 | version = "1.13.2" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 655 | 656 | [[package]] 657 | name = "socket2" 658 | version = "0.5.7" 659 | source = "registry+https://github.com/rust-lang/crates.io-index" 660 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 661 | dependencies = [ 662 | "libc", 663 | "windows-sys 0.52.0", 664 | ] 665 | 666 | [[package]] 667 | name = "stability" 668 | version = "0.2.1" 669 | source = "registry+https://github.com/rust-lang/crates.io-index" 670 | checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" 671 | dependencies = [ 672 | "quote", 673 | "syn", 674 | ] 675 | 676 | [[package]] 677 | name = "static_assertions" 678 | version = "1.1.0" 679 | source = "registry+https://github.com/rust-lang/crates.io-index" 680 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 681 | 682 | [[package]] 683 | name = "strum" 684 | version = "0.26.3" 685 | source = "registry+https://github.com/rust-lang/crates.io-index" 686 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 687 | dependencies = [ 688 | "strum_macros", 689 | ] 690 | 691 | [[package]] 692 | name = "strum_macros" 693 | version = "0.26.4" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 696 | dependencies = [ 697 | "heck", 698 | "proc-macro2", 699 | "quote", 700 | "rustversion", 701 | "syn", 702 | ] 703 | 704 | [[package]] 705 | name = "syn" 706 | version = "2.0.71" 707 | source = "registry+https://github.com/rust-lang/crates.io-index" 708 | checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" 709 | dependencies = [ 710 | "proc-macro2", 711 | "quote", 712 | "unicode-ident", 713 | ] 714 | 715 | [[package]] 716 | name = "thiserror" 717 | version = "1.0.63" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" 720 | dependencies = [ 721 | "thiserror-impl", 722 | ] 723 | 724 | [[package]] 725 | name = "thiserror-impl" 726 | version = "1.0.63" 727 | source = "registry+https://github.com/rust-lang/crates.io-index" 728 | checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" 729 | dependencies = [ 730 | "proc-macro2", 731 | "quote", 732 | "syn", 733 | ] 734 | 735 | [[package]] 736 | name = "tokio" 737 | version = "1.39.1" 738 | source = "registry+https://github.com/rust-lang/crates.io-index" 739 | checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" 740 | dependencies = [ 741 | "backtrace", 742 | "bytes", 743 | "libc", 744 | "mio 1.0.1", 745 | "parking_lot", 746 | "pin-project-lite", 747 | "signal-hook-registry", 748 | "socket2", 749 | "tokio-macros", 750 | "windows-sys 0.52.0", 751 | ] 752 | 753 | [[package]] 754 | name = "tokio-macros" 755 | version = "2.4.0" 756 | source = "registry+https://github.com/rust-lang/crates.io-index" 757 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 758 | dependencies = [ 759 | "proc-macro2", 760 | "quote", 761 | "syn", 762 | ] 763 | 764 | [[package]] 765 | name = "tokio-serial" 766 | version = "5.4.4" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "aa6e2e4cf0520a99c5f87d5abb24172b5bd220de57c3181baaaa5440540c64aa" 769 | dependencies = [ 770 | "cfg-if", 771 | "futures", 772 | "log", 773 | "mio-serial", 774 | "tokio", 775 | ] 776 | 777 | [[package]] 778 | name = "unescaper" 779 | version = "0.1.5" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "c878a167baa8afd137494101a688ef8c67125089ff2249284bd2b5f9bfedb815" 782 | dependencies = [ 783 | "thiserror", 784 | ] 785 | 786 | [[package]] 787 | name = "unicode-ident" 788 | version = "1.0.12" 789 | source = "registry+https://github.com/rust-lang/crates.io-index" 790 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 791 | 792 | [[package]] 793 | name = "unicode-segmentation" 794 | version = "1.11.0" 795 | source = "registry+https://github.com/rust-lang/crates.io-index" 796 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 797 | 798 | [[package]] 799 | name = "unicode-truncate" 800 | version = "1.1.0" 801 | source = "registry+https://github.com/rust-lang/crates.io-index" 802 | checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" 803 | dependencies = [ 804 | "itertools", 805 | "unicode-segmentation", 806 | "unicode-width", 807 | ] 808 | 809 | [[package]] 810 | name = "unicode-width" 811 | version = "0.1.13" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 814 | 815 | [[package]] 816 | name = "version_check" 817 | version = "0.9.4" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 820 | 821 | [[package]] 822 | name = "wasi" 823 | version = "0.11.0+wasi-snapshot-preview1" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 826 | 827 | [[package]] 828 | name = "winapi" 829 | version = "0.3.9" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 832 | dependencies = [ 833 | "winapi-i686-pc-windows-gnu", 834 | "winapi-x86_64-pc-windows-gnu", 835 | ] 836 | 837 | [[package]] 838 | name = "winapi-i686-pc-windows-gnu" 839 | version = "0.4.0" 840 | source = "registry+https://github.com/rust-lang/crates.io-index" 841 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 842 | 843 | [[package]] 844 | name = "winapi-x86_64-pc-windows-gnu" 845 | version = "0.4.0" 846 | source = "registry+https://github.com/rust-lang/crates.io-index" 847 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 848 | 849 | [[package]] 850 | name = "windows-sys" 851 | version = "0.48.0" 852 | source = "registry+https://github.com/rust-lang/crates.io-index" 853 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 854 | dependencies = [ 855 | "windows-targets 0.48.5", 856 | ] 857 | 858 | [[package]] 859 | name = "windows-sys" 860 | version = "0.52.0" 861 | source = "registry+https://github.com/rust-lang/crates.io-index" 862 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 863 | dependencies = [ 864 | "windows-targets 0.52.6", 865 | ] 866 | 867 | [[package]] 868 | name = "windows-targets" 869 | version = "0.48.5" 870 | source = "registry+https://github.com/rust-lang/crates.io-index" 871 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 872 | dependencies = [ 873 | "windows_aarch64_gnullvm 0.48.5", 874 | "windows_aarch64_msvc 0.48.5", 875 | "windows_i686_gnu 0.48.5", 876 | "windows_i686_msvc 0.48.5", 877 | "windows_x86_64_gnu 0.48.5", 878 | "windows_x86_64_gnullvm 0.48.5", 879 | "windows_x86_64_msvc 0.48.5", 880 | ] 881 | 882 | [[package]] 883 | name = "windows-targets" 884 | version = "0.52.6" 885 | source = "registry+https://github.com/rust-lang/crates.io-index" 886 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 887 | dependencies = [ 888 | "windows_aarch64_gnullvm 0.52.6", 889 | "windows_aarch64_msvc 0.52.6", 890 | "windows_i686_gnu 0.52.6", 891 | "windows_i686_gnullvm", 892 | "windows_i686_msvc 0.52.6", 893 | "windows_x86_64_gnu 0.52.6", 894 | "windows_x86_64_gnullvm 0.52.6", 895 | "windows_x86_64_msvc 0.52.6", 896 | ] 897 | 898 | [[package]] 899 | name = "windows_aarch64_gnullvm" 900 | version = "0.48.5" 901 | source = "registry+https://github.com/rust-lang/crates.io-index" 902 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 903 | 904 | [[package]] 905 | name = "windows_aarch64_gnullvm" 906 | version = "0.52.6" 907 | source = "registry+https://github.com/rust-lang/crates.io-index" 908 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 909 | 910 | [[package]] 911 | name = "windows_aarch64_msvc" 912 | version = "0.48.5" 913 | source = "registry+https://github.com/rust-lang/crates.io-index" 914 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 915 | 916 | [[package]] 917 | name = "windows_aarch64_msvc" 918 | version = "0.52.6" 919 | source = "registry+https://github.com/rust-lang/crates.io-index" 920 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 921 | 922 | [[package]] 923 | name = "windows_i686_gnu" 924 | version = "0.48.5" 925 | source = "registry+https://github.com/rust-lang/crates.io-index" 926 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 927 | 928 | [[package]] 929 | name = "windows_i686_gnu" 930 | version = "0.52.6" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 933 | 934 | [[package]] 935 | name = "windows_i686_gnullvm" 936 | version = "0.52.6" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 939 | 940 | [[package]] 941 | name = "windows_i686_msvc" 942 | version = "0.48.5" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 945 | 946 | [[package]] 947 | name = "windows_i686_msvc" 948 | version = "0.52.6" 949 | source = "registry+https://github.com/rust-lang/crates.io-index" 950 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 951 | 952 | [[package]] 953 | name = "windows_x86_64_gnu" 954 | version = "0.48.5" 955 | source = "registry+https://github.com/rust-lang/crates.io-index" 956 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 957 | 958 | [[package]] 959 | name = "windows_x86_64_gnu" 960 | version = "0.52.6" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 963 | 964 | [[package]] 965 | name = "windows_x86_64_gnullvm" 966 | version = "0.48.5" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 969 | 970 | [[package]] 971 | name = "windows_x86_64_gnullvm" 972 | version = "0.52.6" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 975 | 976 | [[package]] 977 | name = "windows_x86_64_msvc" 978 | version = "0.48.5" 979 | source = "registry+https://github.com/rust-lang/crates.io-index" 980 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 981 | 982 | [[package]] 983 | name = "windows_x86_64_msvc" 984 | version = "0.52.6" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 987 | 988 | [[package]] 989 | name = "zerocopy" 990 | version = "0.7.35" 991 | source = "registry+https://github.com/rust-lang/crates.io-index" 992 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 993 | dependencies = [ 994 | "zerocopy-derive", 995 | ] 996 | 997 | [[package]] 998 | name = "zerocopy-derive" 999 | version = "0.7.35" 1000 | source = "registry+https://github.com/rust-lang/crates.io-index" 1001 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1002 | dependencies = [ 1003 | "proc-macro2", 1004 | "quote", 1005 | "syn", 1006 | ] 1007 | --------------------------------------------------------------------------------