From a3c8ef9f74526d727cdf58552daac349fdf4dea3 Mon Sep 17 00:00:00 2001 From: lemonsh Date: Sun, 28 May 2023 18:12:40 +0200 Subject: [PATCH] proper prompt coloring and password masking --- connectbox-shell/Cargo.toml | 4 +- connectbox-shell/src/cli.rs | 4 +- connectbox-shell/src/commands/pfw.rs | 28 ++++++++------ connectbox-shell/src/main.rs | 56 ++++++++++++++++++++++++---- connectbox/src/models.rs | 10 ++--- rustfmt.toml | 2 + 6 files changed, 77 insertions(+), 27 deletions(-) create mode 100644 rustfmt.toml diff --git a/connectbox-shell/Cargo.toml b/connectbox-shell/Cargo.toml index 4015d41..82bf466 100644 --- a/connectbox-shell/Cargo.toml +++ b/connectbox-shell/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" edition = "2021" [dependencies] -rustyline = "11" +rustyline = {version = "11", features = ["derive"]} color-eyre = "0.6" tokio = { version = "1.0", default-features = false, features = ["macros"] } tracing = "0.1" @@ -15,5 +15,5 @@ clap = { version = "4.2", default-features = false, features = ["suggestions", " dirs = "5.0" connectbox = { path = "../connectbox" } color-print = "0.3" -ascii_table = { version = "4.0", features = ["auto_table_width"] } +ascii_table = "4.0" once_cell = "1.17" diff --git a/connectbox-shell/src/cli.rs b/connectbox-shell/src/cli.rs index e63995e..f43340b 100644 --- a/connectbox-shell/src/cli.rs +++ b/connectbox-shell/src/cli.rs @@ -54,8 +54,8 @@ pub(crate) enum PortForwardsCommand { /// ID of the port. You can use `pfw show` to find it id: u32, /// Action to perform with the port. Can be either enable, disable or delete. - action: String - } + action: String, + }, } pub(crate) fn shell_cmd() -> Command { diff --git a/connectbox-shell/src/commands/pfw.rs b/connectbox-shell/src/commands/pfw.rs index 1ea027b..8c429e3 100644 --- a/connectbox-shell/src/commands/pfw.rs +++ b/connectbox-shell/src/commands/pfw.rs @@ -3,7 +3,10 @@ use std::vec; use ascii_table::{Align::Right, AsciiTable}; use color_eyre::Result; use color_print::cprintln; -use connectbox::{models::{PortForwardEntry, PortForwardProtocol}, PortForwardAction}; +use connectbox::{ + models::{PortForwardEntry, PortForwardProtocol}, + PortForwardAction, +}; use once_cell::sync::OnceCell; use crate::{cli::PortForwardsCommand, AppState}; @@ -101,24 +104,27 @@ pub(crate) async fn run(cmd: PortForwardsCommand, state: &AppState) -> Result<() } _ => { cprintln!("Invalid action {action:?}"); - return Ok(()) + return Ok(()); } }; let mut modified = false; - state.connect_box.edit_port_forwards(|p| { - if p.id == id { - modified = true; - action - } else { - PortForwardAction::Keep - } - }).await?; + state + .connect_box + .edit_port_forwards(|p| { + if p.id == id { + modified = true; + action + } else { + PortForwardAction::Keep + } + }) + .await?; if !modified { cprintln!("No port with id {id} exists"); } else { cprintln!("Done!") } - }, + } } Ok(()) } diff --git a/connectbox-shell/src/main.rs b/connectbox-shell/src/main.rs index 37041a9..9f23388 100644 --- a/connectbox-shell/src/main.rs +++ b/connectbox-shell/src/main.rs @@ -1,12 +1,16 @@ -use color_print::{cprintln, cstr}; - +use crate::{cli::ShellCommand, utils::QuotableArgs}; use clap::{FromArgMatches, Parser}; use cli::Args; use color_eyre::Result; +use color_print::{cformat, cprintln, cstr}; use connectbox::ConnectBox; -use rustyline::{error::ReadlineError, DefaultEditor}; - -use crate::{cli::ShellCommand, utils::QuotableArgs}; +use rustyline::{ + error::ReadlineError, + highlight::Highlighter, + history::{DefaultHistory, FileHistory, MemHistory}, + Completer, DefaultEditor, Editor, Helper, Hinter, Validator, +}; +use std::borrow::Cow; mod cli; mod commands; @@ -16,6 +20,40 @@ pub(crate) struct AppState { connect_box: ConnectBox, } +#[derive(Completer, Helper, Hinter, Validator)] +struct GreenPrompt; + +impl Highlighter for GreenPrompt { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + _default: bool, + ) -> std::borrow::Cow<'b, str> { + cformat!("{prompt}").into() + } +} + +#[derive(Completer, Helper, Hinter, Validator)] +struct PasswordPrompt; + +impl Highlighter for PasswordPrompt { + fn highlight_prompt<'b, 's: 'b, 'p: 'b>( + &'s self, + prompt: &'p str, + _default: bool, + ) -> std::borrow::Cow<'b, str> { + cformat!("{prompt}").into() + } + + fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { + "*".repeat(line.len()).into() + } + + fn highlight_char(&self, _line: &str, _pos: usize) -> bool { + true + } +} + #[tokio::main(flavor = "current_thread")] async fn main() -> Result<()> { let args = Args::parse(); @@ -26,10 +64,14 @@ async fn main() -> Result<()> { .with_max_level(args.log_level) .init(); - let mut rl = DefaultEditor::new()?; + let mut rl = Editor::new()?; + rl.set_helper(Some(GreenPrompt)); + let password = if let Some(password) = args.password { password } else { + let mut rl = Editor::new()?; + rl.set_helper(Some(PasswordPrompt)); rl.readline("Password: ")? }; let history_path = dirs::data_dir() @@ -43,7 +85,7 @@ async fn main() -> Result<()> { let state = AppState { connect_box }; loop { - match rl.readline(cstr!("\n > ")) { + match rl.readline("\n >> ") { Ok(line) => { if line.chars().all(char::is_whitespace) { continue; diff --git a/connectbox/src/models.rs b/connectbox/src/models.rs index 29a4a9b..d049cf4 100644 --- a/connectbox/src/models.rs +++ b/connectbox/src/models.rs @@ -1,9 +1,9 @@ -use std::fmt::Display; -use std::net::Ipv4Addr; -use std::time::Duration; +use std::{fmt::Display, net::Ipv4Addr, time::Duration}; -use serde::de::{self, Error, Unexpected}; -use serde::{Deserialize, Deserializer}; +use serde::{ + de::{self, Error, Unexpected}, + Deserialize, Deserializer, +}; #[derive(Deserialize, Debug)] pub struct LanUserTable { diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..5293560 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +unstable_features = true +imports_granularity = "Crate"