diff --git a/crates/ember-cli/src/bench_conn.rs b/crates/ember-cli/src/bench_conn.rs index b183116e..585882e8 100644 --- a/crates/ember-cli/src/bench_conn.rs +++ b/crates/ember-cli/src/bench_conn.rs @@ -11,6 +11,7 @@ use ember_protocol::parse::parse_frame; use ember_protocol::types::Frame; use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use crate::connection::auth_frame; use crate::tls::{self, MaybeTlsStream, TlsClientConfig}; /// Read buffer size for benchmark connections (256 KiB). @@ -49,10 +50,7 @@ impl BenchConnection { /// Authenticates with the server using AUTH. pub async fn authenticate(&mut self, password: &str) -> Result<(), String> { - let frame = Frame::Array(vec![ - Frame::Bulk(Bytes::from_static(b"AUTH")), - Frame::Bulk(Bytes::from(password.to_string())), - ]); + let frame = auth_frame(password); let mut buf = BytesMut::new(); frame.serialize(&mut buf); diff --git a/crates/ember-cli/src/connection.rs b/crates/ember-cli/src/connection.rs index df2b820a..8bcfa210 100644 --- a/crates/ember-cli/src/connection.rs +++ b/crates/ember-cli/src/connection.rs @@ -100,8 +100,12 @@ impl Connection { /// Authenticates with the server using the AUTH command. pub async fn authenticate(&mut self, password: &str) -> Result<(), ConnectionError> { - let tokens = vec!["AUTH".to_string(), password.to_string()]; - let response = self.send_command(&tokens).await?; + let frame = auth_frame(password); + self.write_buf.clear(); + frame.serialize(&mut self.write_buf); + self.stream.write_all(&self.write_buf).await?; + self.stream.flush().await?; + let response = self.read_response().await?; match &response { Frame::Simple(s) if s == "OK" => Ok(()), @@ -167,3 +171,14 @@ impl Connection { } } } + +/// Builds an AUTH command frame from a password string. +/// +/// Shared by the interactive REPL connection and the benchmark connection so +/// the AUTH wire format is defined in exactly one place. +pub fn auth_frame(password: &str) -> Frame { + Frame::Array(vec![ + Frame::Bulk(bytes::Bytes::from_static(b"AUTH")), + Frame::Bulk(bytes::Bytes::from(password.to_string())), + ]) +} diff --git a/crates/ember-cli/src/repl.rs b/crates/ember-cli/src/repl.rs index 01edc786..be1777b5 100644 --- a/crates/ember-cli/src/repl.rs +++ b/crates/ember-cli/src/repl.rs @@ -23,6 +23,14 @@ use crate::connection::{Connection, ConnectionError}; use crate::format::format_response; use crate::tls::TlsClientConfig; +// ANSI escape sequences for terminal coloring. Defined as constants so a +// color change requires updating one place rather than grep-and-replace. +const RESET: &str = "\x1b[0m"; +const GREEN: &str = "\x1b[32m"; +const RED: &str = "\x1b[31m"; +const BOLD_CYAN: &str = "\x1b[1;36m"; +const DIM: &str = "\x1b[2m"; + /// Runs the interactive REPL loop. /// /// Blocks the calling thread. Uses `tokio::runtime::Runtime` internally @@ -449,9 +457,9 @@ impl Highlighter for EmberHelper { || LOCAL_COMMANDS.iter().any(|c| c.eq_ignore_ascii_case(first)); let highlighted_cmd = if is_known { - format!("\x1b[1;36m{first}\x1b[0m") // bold cyan + format!("{BOLD_CYAN}{first}{RESET}") // bold cyan } else { - format!("\x1b[31m{first}\x1b[0m") // red + format!("{RED}{first}{RESET}") // red }; // highlight quoted strings in rest @@ -474,7 +482,7 @@ impl Highlighter for EmberHelper { } fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> { - Cow::Owned(format!("\x1b[2m{hint}\x1b[0m")) // dim + Cow::Owned(format!("{DIM}{hint}{RESET}")) // dim } fn highlight_char( @@ -495,7 +503,8 @@ fn highlight_quotes(s: &str) -> String { while let Some(ch) = chars.next() { match ch { '"' => { - out.push_str("\x1b[32m\""); // green + out.push_str(GREEN); + out.push('"'); loop { match chars.next() { None => break, @@ -512,10 +521,11 @@ fn highlight_quotes(s: &str) -> String { Some(c) => out.push(c), } } - out.push_str("\x1b[0m"); // reset + out.push_str(RESET); } '\'' => { - out.push_str("\x1b[32m'"); // green + out.push_str(GREEN); + out.push('\''); loop { match chars.next() { None => break, @@ -526,7 +536,7 @@ fn highlight_quotes(s: &str) -> String { Some(c) => out.push(c), } } - out.push_str("\x1b[0m"); // reset + out.push_str(RESET); } _ => out.push(ch), }