From 119af02dfaf2b3fd9e2e49c8aff22f13d38fe986 Mon Sep 17 00:00:00 2001 From: John Doty Date: Mon, 10 Oct 2022 02:51:43 +0000 Subject: [PATCH] Starting to build a UI --- Cargo.lock | 47 ++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/lib.rs | 84 +++++++++++++++++++++++++++++++++--------------------- src/ui.rs | 34 ++++++++++++++++++++++ 4 files changed, 133 insertions(+), 33 deletions(-) create mode 100644 src/ui.rs diff --git a/Cargo.lock b/Cargo.lock index ccac33e..6997254 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c" +dependencies = [ + "winapi", +] + [[package]] name = "errno" version = "0.2.8" @@ -132,6 +157,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bytes", + "crossterm", "procfs", "thiserror", "tokio", @@ -370,6 +396,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "signal-hook" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a253b5e89e2698464fc26b545c9edceb338e18a89effeeecfea192c3025be29d" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index f45d97f..33b5a89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ anyhow = "1.0" bytes = "1" thiserror = "1.0" tokio = { version = "1", features = ["full"] } +crossterm = "0.25" [target.'cfg(target_os="linux")'.dependencies] procfs = "0.14.1" diff --git a/src/lib.rs b/src/lib.rs index a6983cb..c7fbfb4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ use tokio::sync::oneshot; mod connection; mod message; mod refresh; +mod ui; // ---------------------------------------------------------------------------- // Write Management @@ -60,7 +61,7 @@ async fn server_handle_connection( let mut writer = writer.clone(); connection::process(channel, &mut stream, &mut data, &mut writer).await; - eprintln!("< Done server!"); + // eprintln!("< Done server!"); } } } @@ -70,7 +71,7 @@ async fn server_read( writer: mpsc::Sender, connections: ConnectionTable, ) -> Result<()> { - eprintln!("< Processing packets..."); + // eprintln!("< Processing packets..."); loop { let message = reader.read().await?; @@ -104,14 +105,14 @@ async fn server_read( tokio::spawn(async move { let ports = match refresh::get_entries() { Ok(ports) => ports, - Err(e) => { - eprintln!("< Error scanning: {:?}", e); + Err(_) => { + // eprintln!("< Error scanning: {:?}", e); vec![] } }; - if let Err(e) = writer.send(Message::Ports(ports)).await { + if let Err(_) = writer.send(Message::Ports(ports)).await { // Writer has been closed for some reason, we can just quit.... I hope everything is OK? - eprintln!("< Warning: Error sending: {:?}", e); + // eprintln!("< Warning: Error sending: {:?}", e); } }); } @@ -165,7 +166,7 @@ async fn client_sync( reader: &mut Read, writer: &mut Write, ) -> Result<(), tokio::io::Error> { - eprintln!("> Waiting for synchronization marker..."); + // eprintln!("> Waiting for synchronization marker..."); // Run these two loops in parallel; the copy of stdin should stop when // we've seen the marker from the client. If the pipe closes for whatever @@ -211,9 +212,9 @@ async fn client_handle_connection( let mut writer = writer.clone(); connection::process(channel, socket, &mut data, &mut writer).await; - eprintln!("> Done client!"); + // eprintln!("> Done client!"); } else { - eprintln!("> Failed to connect to remote"); + // eprintln!("> Failed to connect to remote"); } } } @@ -242,10 +243,11 @@ async fn client_read( reader: &mut MessageReader, writer: mpsc::Sender, connections: ConnectionTable, + port_sender: mpsc::Sender>, ) -> Result<()> { let mut listeners: HashMap> = HashMap::new(); - eprintln!("> Processing packets..."); + // eprintln!("> Processing packets..."); loop { let message = reader.read().await?; @@ -272,11 +274,7 @@ async fn client_read( } Ports(ports) => { let mut new_listeners = HashMap::new(); - - println!("The following ports are available:"); - for port in ports { - println!(" {}: {}", port.port, port.desc); - + for port in &ports { let port = port.port; if let Some(l) = listeners.remove(&port) { if !l.is_closed() { @@ -300,14 +298,17 @@ async fn client_read( r = client_listen(port, writer, connections) => r, _ = stop => Ok(()), }; - if let Err(e) = result { - eprintln!("> Error listening on port {}: {:?}", port, e); + if let Err(_) = result { + // eprintln!("> Error listening on port {}: {:?}", port, e); } }); } } listeners = new_listeners; + if let Err(_) = port_sender.send(ports).await { + // TODO: Log + } } _ => panic!("Unsupported: {:?}", message), }; @@ -328,41 +329,58 @@ async fn client_main( } // Kick things off with a listing of the ports... - eprintln!("> Sending initial list command..."); - writer.write(Message::Refresh).await?; + // eprintln!("> Sending initial list command..."); + // writer.write(Message::Refresh).await?; + let (port_sender, mut port_receiver) = mpsc::channel(2); let connections = ConnectionTable::new(); + let ui = tokio::spawn(async move { ui::run_ui(&mut port_receiver).await }); + // And now really get into it... let (msg_sender, mut msg_receiver) = mpsc::channel(32); + let refresher = msg_sender.clone(); // Special for loop. + let writing = pump_write(&mut msg_receiver, writer); - let reading = client_read(reader, msg_sender, connections); + let reading = client_read(reader, msg_sender, connections, port_sender); tokio::pin!(reading); tokio::pin!(writing); let (mut done_writing, mut done_reading) = (false, false); - loop { + while !(done_reading && done_writing) { tokio::select! { + result = async { + loop { + use tokio::time::{sleep, Duration}; + if let Err(e) = refresher.send(Message::Refresh).await { + break Err::<(), _>(e); + } + sleep(Duration::from_millis(100)).await; + } + }, if !done_writing => { + if let Err(e) = result { + return Err(e.into()); + } + }, result = &mut writing, if !done_writing => { done_writing = true; if let Err(e) = result { return Err(e); } - if done_reading && done_writing { - return Ok(()); - } }, result = &mut reading, if !done_reading => { done_reading = true; if let Err(e) = result { return Err(e); } - if done_reading && done_writing { - return Ok(()); - } }, } } + + if let Err(_) = ui.await { + //TODO + } + Ok(()) } ///// @@ -372,22 +390,22 @@ pub async fn run_server() { let mut writer = BufWriter::new(tokio::io::stdout()); // Write the 8-byte synchronization marker. - eprintln!("< Writing marker..."); + // eprintln!("< Writing marker..."); writer .write_u64(0x00_00_00_00_00_00_00_00) .await .expect("Error writing marker"); - if let Err(e) = writer.flush().await { - eprintln!("Error writing sync marker: {:?}", e); + if let Err(_) = writer.flush().await { + // eprintln!("Error writing sync marker: {:?}", e); return; } - eprintln!("< Done!"); + // eprintln!("< Done!"); let mut writer = MessageWriter::new(writer); let mut reader = MessageReader::new(reader); - if let Err(e) = server_main(&mut reader, &mut writer).await { - eprintln!("Error: {:?}", e); + if let Err(_) = server_main(&mut reader, &mut writer).await { + // eprintln!("Error: {:?}", e); } } diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 0000000..198646a --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,34 @@ +use crate::message::PortDesc; +use anyhow::Result; +use crossterm::{ + cursor::MoveTo, + execute, + terminal::{ + Clear, ClearType, DisableLineWrap, EnableLineWrap, EnterAlternateScreen, + LeaveAlternateScreen, + }, +}; +use std::io::stdout; +use tokio::sync::mpsc; + +pub async fn run_ui(port_receiver: &mut mpsc::Receiver>) -> Result<()> { + let mut stdout = stdout(); + execute!(stdout, EnterAlternateScreen, DisableLineWrap)?; + while let Some(mut ports) = port_receiver.recv().await { + ports.sort_by(|a, b| a.port.partial_cmp(&b.port).unwrap()); + + execute!(stdout, Clear(ClearType::All), MoveTo(0, 0))?; + println!("Port Url Description"); + println!("----- ------------------------ -----------"); + for port in ports { + println!( + "{:5} {:24} {}", + port.port, + format!("http://locahost:{}/", port.port), + port.desc + ); + } + } + execute!(stdout, EnableLineWrap, LeaveAlternateScreen)?; + Ok(()) +}