proper prompt coloring and password masking
This commit is contained in:
		| @@ -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" | ||||||
|   | |||||||
| @@ -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 { | ||||||
|   | |||||||
| @@ -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 | ||||||
|  |                 .connect_box | ||||||
|  |                 .edit_port_forwards(|p| { | ||||||
|                     if p.id == id { |                     if p.id == id { | ||||||
|                         modified = true; |                         modified = true; | ||||||
|                         action |                         action | ||||||
|                     } else { |                     } else { | ||||||
|                         PortForwardAction::Keep |                         PortForwardAction::Keep | ||||||
|                     } |                     } | ||||||
|             }).await?; |                 }) | ||||||
|  |                 .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(()) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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; | ||||||
|   | |||||||
| @@ -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
									
								
							
							
						
						
									
										2
									
								
								rustfmt.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | |||||||
|  | unstable_features = true | ||||||
|  | imports_granularity = "Crate" | ||||||
		Reference in New Issue
	
	Block a user