Skip to content

Commit

Permalink
refactor(cli): convert run command to crossterm (#153)
Browse files Browse the repository at this point in the history
  • Loading branch information
hougesen authored Feb 6, 2024
1 parent 3f11d90 commit d6c8841
Show file tree
Hide file tree
Showing 10 changed files with 185 additions and 73 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@
.trunk
.vscode
.idea

mutants.out*
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ build:
cargo build
cargo build --release


build-local:
make build
sudo cp target/release/hitt /usr/local/bin/hitt-local

lint:
cargo fmt -- --check --color always
cargo clippy --all-targets --all-features -- -D warnings
Expand Down
1 change: 1 addition & 0 deletions hitt-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ path = "src/main.rs"
[dependencies]
clap = { version = "4.4.18", features = ["derive"] }
console = { version = "0.15.8" }
crossterm = "0.27.0"
hitt-formatter = { workspace = true }
hitt-parser = { workspace = true }
hitt-request = { workspace = true }
Expand Down
17 changes: 12 additions & 5 deletions hitt-cli/src/commands/run.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crossterm::{style::Print, style::Stylize, QueueableCommand};
use hitt_request::send_request;

use crate::{
Expand All @@ -7,8 +8,8 @@ use crate::{
terminal::handle_response,
};

pub async fn run_command(
term: &console::Term,
pub async fn run_command<W: std::io::Write + Send>(
term: &mut W,
args: &RunCommandArguments,
) -> Result<(), HittCliError> {
let http_client = reqwest::Client::new();
Expand All @@ -33,12 +34,19 @@ pub async fn run_command(

let parsed_files = parse_requests_threaded(http_file_paths, vars).await?;

let mut request_count: u16 = 0;

for (path, file) in parsed_files {
if !args.vim {
term.write_line(&format!("hitt: running {path:?}"))?;
term.queue(Print(format!("hitt: running {path:?}\n\n").cyan()))?;
term.flush()?;
}

for req in file {
if request_count > 0 {
term.queue(Print("\n"))?;
}

match send_request(&http_client, &req, &timeout).await {
Ok(response) => handle_response(term, &response, args),
Err(request_error) => {
Expand All @@ -49,10 +57,9 @@ pub async fn run_command(
Err(HittCliError::Reqwest(req.method, req.uri, request_error))
}
}?;
request_count += 1;
}
}

term.flush()?;

Ok(())
}
4 changes: 1 addition & 3 deletions hitt-cli/src/error/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use core::fmt;

use console::style;

#[derive(Debug)]
pub enum HittCliError {
Parse(std::path::PathBuf, hitt_parser::error::RequestParseError),
Expand Down Expand Up @@ -38,7 +36,7 @@ impl fmt::Display for HittCliError {
}
};

write!(f, "{}", style(format!("hitt: {error_message}")).red())
write!(f, "hitt: {error_message}")
}
}

Expand Down
14 changes: 10 additions & 4 deletions hitt-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
use std::io::{stdout, Write};

use clap::Parser;
use crossterm::{
queue,
style::{Print, Stylize},
};

use self::{
commands::{new::new_command, run::run_command},
Expand All @@ -16,15 +22,15 @@ mod terminal;
async fn main() -> Result<(), HittCliError> {
let cli = Cli::parse();

let term = console::Term::stdout();
let mut term = stdout();

let command_result = match &cli.command {
Commands::Run(args) => run_command(&term, args).await,
Commands::New(args) => new_command(&term, args).await,
Commands::Run(args) => run_command(&mut term, args).await,
Commands::New(args) => new_command(&console::Term::stdout(), args).await,
};

if let Err(err) = command_result {
term.write_line(&err.to_string())?;
queue!(term, Print(err.to_string().red().bold()), Print("\n"))?;
}

term.flush()?;
Expand Down
93 changes: 71 additions & 22 deletions hitt-cli/src/terminal/body.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
use crossterm::{
queue,
style::{Print, Stylize},
};
use hitt_formatter::ContentType;

#[inline]
fn __print_body(term: &console::Term, body: &str) -> Result<(), std::io::Error> {
term.write_line(&format!("\n{}", console::style(body).yellow()))
fn __print_body<W: std::io::Write>(term: &mut W, body: &str) -> Result<(), std::io::Error> {
queue!(term, Print("\n"), Print(body.dark_yellow()), Print("\n\n"))
}

#[cfg(test)]
mod __test_print_body {
use std::io::Write;

use super::__print_body;

#[test]
fn it_should_print_without_errors() {
let term = console::Term::stdout();
let mut term = Vec::new();

__print_body(&mut term, "body").expect("it not to return an error");

term.flush().expect("it to flush");

// TODO: validate what is written to stdout
__print_body(&term, "body").expect("it not to return an error");
assert_eq!(
"\n\x1B[38;5;3mbody\x1B[39m\n\n",
String::from_utf8_lossy(&term)
);

term.clear();
}
}

#[inline]
pub fn print_body(
term: &console::Term,
pub fn print_body<W: std::io::Write>(
term: &mut W,
body: &str,
content_type: ContentType,
disable_pretty_printing: bool,
Expand All @@ -38,39 +52,74 @@ pub fn print_body(

#[cfg(test)]
mod test_print_body {
use std::io::Write;

use hitt_formatter::ContentType;

use super::print_body;

#[test]
fn it_should_print_without_errors() {
let term = console::Term::stdout();

print_body(&term, "body", ContentType::Unknown, false).expect("it not to return an error");

print_body(&term, "body", ContentType::Unknown, true).expect("it not to return an error");
let mut term = Vec::new();

print_body(&mut term, "body", ContentType::Unknown, false)
.expect("it not to return an error");

term.flush().expect("it to flush");
assert_eq!(
"\n\x1B[38;5;3mbody\x1B[39m\n\n",
String::from_utf8_lossy(&term)
);
term.clear();

print_body(&mut term, "body", ContentType::Unknown, true)
.expect("it not to return an error");

term.flush().expect("it to flush");
assert_eq!(
"\n\x1B[38;5;3mbody\x1B[39m\n\n",
String::from_utf8_lossy(&term)
);
term.clear();
}

#[test]
fn it_should_format_json() {
let term = console::Term::stdout();
let mut term = Vec::new();

let input = "{\"key\":\"value\"}";

// TODO: test stdout is formatted
print_body(&term, input, ContentType::Json, false).expect("it not to return an error");

// TODO: test stdout is not formatted
print_body(&term, input, ContentType::Json, true).expect("it not to return an error");
print_body(&mut term, input, ContentType::Json, false).expect("it not to return an error");

term.flush().expect("it to flush");
assert_eq!(
"\n\x1B[38;5;3m{\n \"key\": \"value\"\n}\n\x1B[39m\n\n",
String::from_utf8_lossy(&term)
);
term.clear();

print_body(&mut term, input, ContentType::Json, true).expect("it not to return an error");
term.flush().expect("it to flush");
assert_eq!(
format!("\n\x1B[38;5;3m{input}\x1B[39m\n\n"),
String::from_utf8_lossy(&term)
);
term.clear();
}

#[test]
fn it_should_format_json_if_pretty_printing_disable() {
let term = console::Term::stdout();
fn it_not_should_format_json_if_pretty_printing_disable() {
let mut term = Vec::new();

let input = "{\"key\":\"value\"}";

// TODO: test stdout is not formatted
print_body(&term, input, ContentType::Json, true).expect("it not to return an error");
print_body(&mut term, input, ContentType::Json, true).expect("it not to return an error");

term.flush().expect("it to flush");
assert_eq!(
format!("\n\x1B[38;5;3m{input}\x1B[39m\n\n"),
String::from_utf8_lossy(&term)
);
term.clear();
}
}
47 changes: 30 additions & 17 deletions hitt-cli/src/terminal/headers.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
use crossterm::{
queue,
style::{Print, Stylize},
};

#[inline]
pub fn print_headers(
term: &console::Term,
pub fn print_headers<W: std::io::Write>(
term: &mut W,
headers: &reqwest::header::HeaderMap,
) -> Result<(), std::io::Error> {
let header_name_style = console::Style::new().yellow();

for (key, value) in headers {
if let Ok(value_str) = value.to_str() {
term.write_line(&format!("{}: {value_str}", header_name_style.apply_to(key)))?;
queue!(
term,
Print(format!("{}: {value_str}\n", key.to_string().dark_yellow()))
)?;
} else {
term.write_line(
&console::style(format!(
"hitt: error printing value for header - {key} '{value:?}'"
))
.red()
.to_string(),
queue!(
term,
Print(format!("hitt: error printing value for header - {key} '{value:?}\n'").red())
)?;
}
}
Expand All @@ -24,22 +27,32 @@ pub fn print_headers(

#[cfg(test)]
mod test_print_headers {
use std::io::Write;

use reqwest::header::{HeaderMap, HeaderName, HeaderValue};

use super::print_headers;

#[test]
fn it_should_print_without_errors() {
let term = console::Term::stdout();
let mut term = Vec::new();

let mut headers = HeaderMap::new();

headers.insert(
HeaderName::from_static("mads"),
HeaderValue::from_static("hougesen"),
let n = "mads";
let v = "hougesen";

headers.insert(HeaderName::from_static(n), HeaderValue::from_static(v));

print_headers(&mut term, &headers).expect("it to not error");

term.flush().expect("it to flush");

assert_eq!(
format!("\x1B[38;5;3m{n}\x1B[39m: {v}\n"),
String::from_utf8_lossy(&term)
);

// TODO: validate what is written to stdout
print_headers(&term, &headers).expect("it to not error");
term.clear();
}
}
4 changes: 2 additions & 2 deletions hitt-cli/src/terminal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ pub mod headers;
pub mod input;
pub mod status;

pub fn handle_response(
term: &console::Term,
pub fn handle_response<W: std::io::Write>(
term: &mut W,
response: &HittResponse,
args: &RunCommandArguments,
) -> Result<(), HittCliError> {
Expand Down
Loading

0 comments on commit d6c8841

Please sign in to comment.