proper prompt coloring and password masking

This commit is contained in:
lemonsh 2023-05-28 18:12:40 +02:00
parent 9703d984bb
commit a3c8ef9f74
6 changed files with 77 additions and 27 deletions

View File

@ -6,7 +6,7 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
rustyline = "11" rustyline = {version = "11", features = ["derive"]}
color-eyre = "0.6" color-eyre = "0.6"
tokio = { version = "1.0", default-features = false, features = ["macros"] } tokio = { version = "1.0", default-features = false, features = ["macros"] }
tracing = "0.1" tracing = "0.1"
@ -15,5 +15,5 @@ clap = { version = "4.2", default-features = false, features = ["suggestions", "
dirs = "5.0" dirs = "5.0"
connectbox = { path = "../connectbox" } connectbox = { path = "../connectbox" }
color-print = "0.3" color-print = "0.3"
ascii_table = { version = "4.0", features = ["auto_table_width"] } ascii_table = "4.0"
once_cell = "1.17" once_cell = "1.17"

View File

@ -54,8 +54,8 @@ pub(crate) enum PortForwardsCommand {
/// ID of the port. You can use `pfw show` to find it /// ID of the port. You can use `pfw show` to find it
id: u32, id: u32,
/// Action to perform with the port. Can be either enable, disable or delete. /// Action to perform with the port. Can be either enable, disable or delete.
action: String action: String,
} },
} }
pub(crate) fn shell_cmd() -> Command { pub(crate) fn shell_cmd() -> Command {

View File

@ -3,7 +3,10 @@ use std::vec;
use ascii_table::{Align::Right, AsciiTable}; use ascii_table::{Align::Right, AsciiTable};
use color_eyre::Result; use color_eyre::Result;
use color_print::cprintln; use color_print::cprintln;
use connectbox::{models::{PortForwardEntry, PortForwardProtocol}, PortForwardAction}; use connectbox::{
models::{PortForwardEntry, PortForwardProtocol},
PortForwardAction,
};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use crate::{cli::PortForwardsCommand, AppState}; use crate::{cli::PortForwardsCommand, AppState};
@ -101,24 +104,27 @@ pub(crate) async fn run(cmd: PortForwardsCommand, state: &AppState) -> Result<()
} }
_ => { _ => {
cprintln!("<red!>Invalid action {action:?}"); cprintln!("<red!>Invalid action {action:?}");
return Ok(()) return Ok(());
} }
}; };
let mut modified = false; let mut modified = false;
state.connect_box.edit_port_forwards(|p| { state
if p.id == id { .connect_box
modified = true; .edit_port_forwards(|p| {
action if p.id == id {
} else { modified = true;
PortForwardAction::Keep action
} } else {
}).await?; PortForwardAction::Keep
}
})
.await?;
if !modified { if !modified {
cprintln!("<red!>No port with id {id} exists"); cprintln!("<red!>No port with id {id} exists");
} else { } else {
cprintln!("<green!>Done!") cprintln!("<green!>Done!")
} }
}, }
} }
Ok(()) Ok(())
} }

View File

@ -1,12 +1,16 @@
use color_print::{cprintln, cstr}; use crate::{cli::ShellCommand, utils::QuotableArgs};
use clap::{FromArgMatches, Parser}; use clap::{FromArgMatches, Parser};
use cli::Args; use cli::Args;
use color_eyre::Result; use color_eyre::Result;
use color_print::{cformat, cprintln, cstr};
use connectbox::ConnectBox; use connectbox::ConnectBox;
use rustyline::{error::ReadlineError, DefaultEditor}; use rustyline::{
error::ReadlineError,
use crate::{cli::ShellCommand, utils::QuotableArgs}; highlight::Highlighter,
history::{DefaultHistory, FileHistory, MemHistory},
Completer, DefaultEditor, Editor, Helper, Hinter, Validator,
};
use std::borrow::Cow;
mod cli; mod cli;
mod commands; mod commands;
@ -16,6 +20,40 @@ pub(crate) struct AppState {
connect_box: ConnectBox, 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!("<green!>{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!("<red!>{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")] #[tokio::main(flavor = "current_thread")]
async fn main() -> Result<()> { async fn main() -> Result<()> {
let args = Args::parse(); let args = Args::parse();
@ -26,10 +64,14 @@ async fn main() -> Result<()> {
.with_max_level(args.log_level) .with_max_level(args.log_level)
.init(); .init();
let mut rl = DefaultEditor::new()?; let mut rl = Editor::new()?;
rl.set_helper(Some(GreenPrompt));
let password = if let Some(password) = args.password { let password = if let Some(password) = args.password {
password password
} else { } else {
let mut rl = Editor::new()?;
rl.set_helper(Some(PasswordPrompt));
rl.readline("Password: ")? rl.readline("Password: ")?
}; };
let history_path = dirs::data_dir() let history_path = dirs::data_dir()
@ -43,7 +85,7 @@ async fn main() -> Result<()> {
let state = AppState { connect_box }; let state = AppState { connect_box };
loop { loop {
match rl.readline(cstr!("\n<green!> > ")) { match rl.readline("\n >> ") {
Ok(line) => { Ok(line) => {
if line.chars().all(char::is_whitespace) { if line.chars().all(char::is_whitespace) {
continue; continue;

View File

@ -1,9 +1,9 @@
use std::fmt::Display; use std::{fmt::Display, net::Ipv4Addr, time::Duration};
use std::net::Ipv4Addr;
use std::time::Duration;
use serde::de::{self, Error, Unexpected}; use serde::{
use serde::{Deserialize, Deserializer}; de::{self, Error, Unexpected},
Deserialize, Deserializer,
};
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct LanUserTable { pub struct LanUserTable {

2
rustfmt.toml Normal file
View File

@ -0,0 +1,2 @@
unstable_features = true
imports_granularity = "Crate"