Better UI, needs two keys to close :(

This commit is contained in:
John Doty 2022-10-10 05:33:27 +00:00
parent 2d9b9dbf9a
commit de6607eb25
4 changed files with 106 additions and 21 deletions

19
Cargo.lock generated
View file

@ -103,6 +103,7 @@ checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crossterm_winapi", "crossterm_winapi",
"futures-core",
"libc", "libc",
"mio", "mio",
"parking_lot", "parking_lot",
@ -151,6 +152,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "futures-core"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e5aa3de05362c3fb88de6531e6296e85cde7739cccad4b9dfeeb7f6ebce56bf"
[[package]] [[package]]
name = "fwd" name = "fwd"
version = "0.1.0" version = "0.1.0"
@ -161,6 +168,7 @@ dependencies = [
"procfs", "procfs",
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-stream",
] ]
[[package]] [[package]]
@ -515,6 +523,17 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "tokio-stream"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6edf2d6bc038a43d31353570e27270603f4648d18f5ed10c0e179abe43255af"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.4" version = "1.0.4"

View file

@ -8,9 +8,10 @@ edition = "2021"
[dependencies] [dependencies]
anyhow = "1.0" anyhow = "1.0"
bytes = "1" bytes = "1"
crossterm = { version = "0.25", features = ["event-stream"] }
thiserror = "1.0" thiserror = "1.0"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
crossterm = "0.25" tokio-stream = "0.1"
[target.'cfg(target_os="linux")'.dependencies] [target.'cfg(target_os="linux")'.dependencies]
procfs = "0.14.1" procfs = "0.14.1"

View file

@ -359,7 +359,7 @@ async fn client_main<Reader: AsyncRead + Unpin, Writer: AsyncWrite + Unpin>(
if let Err(e) = refresher.send(Message::Refresh).await { if let Err(e) = refresher.send(Message::Refresh).await {
break Err::<(), _>(e); break Err::<(), _>(e);
} }
sleep(Duration::from_millis(100)).await; sleep(Duration::from_millis(500)).await;
} }
}, if !done_writing => { }, if !done_writing => {
if let Err(e) = result { if let Err(e) = result {

View file

@ -2,33 +2,98 @@ use crate::message::PortDesc;
use anyhow::Result; use anyhow::Result;
use crossterm::{ use crossterm::{
cursor::MoveTo, cursor::MoveTo,
execute, event::{Event, EventStream, KeyCode, KeyEvent},
execute, queue,
style::Stylize,
terminal::{ terminal::{
Clear, ClearType, DisableLineWrap, EnableLineWrap, EnterAlternateScreen, disable_raw_mode, enable_raw_mode, size, Clear, ClearType, DisableLineWrap, EnableLineWrap,
LeaveAlternateScreen, EnterAlternateScreen, LeaveAlternateScreen,
}, },
}; };
use std::io::stdout; use std::io::{stdout, Write};
use tokio::sync::mpsc; use tokio::sync::mpsc;
use tokio_stream::StreamExt;
pub async fn run_ui(port_receiver: &mut mpsc::Receiver<Vec<PortDesc>>) -> Result<()> { pub async fn run_ui(port_receiver: &mut mpsc::Receiver<Vec<PortDesc>>) -> Result<()> {
let mut stdout = stdout(); enable_raw_mode()?;
execute!(stdout, EnterAlternateScreen, DisableLineWrap)?; let result = run_ui_core(port_receiver).await;
while let Some(mut ports) = port_receiver.recv().await { execute!(stdout(), EnableLineWrap, LeaveAlternateScreen)?;
ports.sort_by(|a, b| a.port.partial_cmp(&b.port).unwrap()); disable_raw_mode()?;
result
}
execute!(stdout, Clear(ClearType::All), MoveTo(0, 0))?; async fn run_ui_core(port_receiver: &mut mpsc::Receiver<Vec<PortDesc>>) -> Result<()> {
println!("Port Url Description"); let mut stdout = stdout();
println!("----- ------------------------ -----------");
execute!(stdout, EnterAlternateScreen, DisableLineWrap)?;
let mut events = EventStream::new();
let mut ports = None;
loop {
tokio::select! {
ev = events.next() => {
match ev {
Some(Ok(Event::Key(ev))) => {
match ev {
KeyEvent {code:KeyCode::Esc, ..} => { break; },
KeyEvent {code:KeyCode::Char('q'), ..} => { break; },
_ => ()
}
},
Some(Ok(_)) => (), // Don't care about this event...
Some(Err(_)) => (), // Hmmmmmm.....?
None => (), // ....no events? what?
}
}
pr = port_receiver.recv() => {
match pr {
Some(p) => { ports = Some(p); }
None => break,
}
}
}
let (columns, rows) = size()?;
queue!(stdout, Clear(ClearType::All), MoveTo(0, 0))?;
// How wide are all the things?
let columns: usize = columns.into();
let padding = 1;
let port_width = 5; // 5 characters for 16-bit number
let url_width = "http://localhost:/".len() + port_width;
let description_width = columns - (padding + port_width + padding + url_width + padding);
print!(
"{}",
format!(
" {port:>port_width$} {url:<url_width$} {description:<description_width$}\r\n",
port = "port",
url = "url",
description = "description"
)
.negative()
);
if let Some(ports) = &mut ports {
ports.sort_by(|a, b| a.port.partial_cmp(&b.port).unwrap());
for port in ports { for port in ports {
println!( print!(
"{:5} {:24} {}", " {:port_width$} {:url_width$} {:description_width$}\r\n",
port.port, port.port,
format!("http://locahost:{}/", port.port), format!("http://locahost:{}/", port.port),
port.desc port.desc
); );
} }
} }
execute!(stdout, EnableLineWrap, LeaveAlternateScreen)?;
queue!(stdout, MoveTo(0, rows - 1))?;
print!(
"{}",
format!("{:columns$}", " Press ESC or q to quit").negative()
);
stdout.flush()?;
}
Ok(()) Ok(())
} }