From 0f219b94e89ad8d000d96e7b7eebcab5dd790aa9 Mon Sep 17 00:00:00 2001 From: John Freeman <371481+johnnyfreeman@users.noreply.github.com> Date: Sun, 22 Dec 2024 14:50:08 -0500 Subject: [PATCH 1/4] updated return signature to reflect the specific error that is returned --- src/executor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/executor.rs b/src/executor.rs index ce77fe6..d68e7f7 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -76,7 +76,7 @@ impl Executor { } } - pub async fn execute(&mut self) -> Result<(), Box> { + pub async fn execute(&mut self) -> Result<(), ExecutionError> { match &self.options.request { Some(request_name) => { let request = self From f7301f2af4b897d15f98b9889c9c5a0dce328ba3 Mon Sep 17 00:00:00 2001 From: John Freeman <371481+johnnyfreeman@users.noreply.github.com> Date: Mon, 23 Dec 2024 02:42:25 -0500 Subject: [PATCH 2/4] moved default cli usage into subcommands to make room for more functionality --- src/cli.rs | 82 +++++++++++++++++++++++++++++++++++++++++++------ src/executor.rs | 65 ++++++++++++++++----------------------- src/main.rs | 43 +++++++++++++++++++++++--- 3 files changed, 137 insertions(+), 53 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 0cdfb23..5d95918 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,10 +1,74 @@ -use clap::Command; - -pub fn get_cli_matches() -> clap::ArgMatches { - Command::new("glint") - .version("0.1.0") - .author("John Freeman") - .about("Send HTTP requests based on a request chain defined in TOML") - .allow_external_subcommands(true) - .get_matches() +use clap::{Args, Parser, Subcommand}; + +#[derive(Parser)] +#[command(version, about, long_about = None)] +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} + +#[derive(Subcommand)] +pub enum Commands { + /// does testing things + Collection { + #[command(subcommand)] + command: CollectionCommands, + }, + /// does testing things + Request { + #[command(subcommand)] + command: RequestCommands, + }, +} + +#[derive(Subcommand)] +pub enum CollectionCommands { + /// Does testing things + Run { + /// The collection file to use + collection: String, + + /// Additional output options + #[command(flatten)] + output_options: OutputOptions, + }, +} + +#[derive(Args, Clone)] +pub struct OutputOptions { + /// Displays the HTTP headers in the output (disabled by default) + #[arg(short = 'h', long, default_value_t = false)] + pub show_headers: bool, + + /// Suppresses the HTTP response status (enabled by default) + #[arg(short = 's', long, default_value_t = false)] + pub hide_status: bool, + + /// Suppresses the HTTP response body (enabled by default) + #[arg(short = 'b', long, default_value_t = false)] + pub hide_body: bool, + + /// Disables pretty-printing for the HTTP response (enabled by default) + #[arg(short = 'r', long, default_value_t = false)] + pub raw_output: bool, + + /// Disables pre-output masking (enabled by default) + #[arg(short = 'm', long, default_value_t = false)] + pub disable_masking: bool, +} + +#[derive(Subcommand)] +pub enum RequestCommands { + /// does testing things + Run { + /// The collection file to use + collection: String, + + /// The specific request to execute within the collection + request: String, + + /// Additional output options + #[command(flatten)] + output_options: OutputOptions, + }, } diff --git a/src/executor.rs b/src/executor.rs index d68e7f7..52715ce 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -1,5 +1,5 @@ +use crate::cli::OutputOptions; use crate::masking::mask_json; -use crate::options::Options; use crate::request::{Dependencies, Dependency, Request, RequestBody}; use crate::resolvers::env_var_resolver::EnvVarResolver; use crate::resolvers::one_password_resolver::{OnePasswordResolver, OnePasswordResolverError}; @@ -52,7 +52,6 @@ static ENV_FILES_CACHE: Lazy>>> = #[derive(Debug)] pub struct Executor { requests: HashMap, - options: Options, http: Client, env_var_resolver: EnvVarResolver, prompt_resolver: PromptResolver, @@ -61,13 +60,12 @@ pub struct Executor { } impl Executor { - pub fn new(requests: Vec, options: Options) -> Self { + pub fn new(requests: Vec) -> Self { Self { requests: requests .into_iter() .map(|request| (request.name.clone(), request)) .collect(), - options, http: Client::new(), env_var_resolver: EnvVarResolver::new(), prompt_resolver: PromptResolver::new(), @@ -76,32 +74,17 @@ impl Executor { } } - pub async fn execute(&mut self) -> Result<(), ExecutionError> { - match &self.options.request { - Some(request_name) => { - let request = self - .requests - .get(request_name) - .ok_or(ExecutionError::RequestNotFound { - request: request_name.to_owned(), - })? - .clone(); - - let response = self.execute_request(request.clone()).await?; - - self.render_output(response).await?; - } - None => { - let cloned_requests: Vec<_> = self.requests.values().cloned().collect(); - - for request in cloned_requests { - let response = self.execute_request(request.clone()).await?; - - self.render_output(response).await?; - } - } - } - Ok(()) + pub async fn execute_request_named( + &mut self, + request: String, + ) -> Result { + let request = self + .requests + .get(&request) + .cloned() + .ok_or_else(|| ExecutionError::RequestNotFound { request })?; + + self.execute_request(request).await } pub async fn execute_request(&mut self, request: Request) -> Result { @@ -220,9 +203,13 @@ impl Executor { Ok(response) } - async fn render_output(&mut self, response: Response) -> Result<(), ExecutionError> { - if !self.options.hide_status { - if self.options.raw_output { + pub async fn render_output( + &mut self, + response: Response, + options: OutputOptions, + ) -> Result<(), ExecutionError> { + if !options.hide_status { + if options.raw_output { println!( "{}: {}", response.status.as_str(), @@ -249,10 +236,10 @@ impl Executor { } } - if self.options.show_headers { + if options.show_headers { let mut headers = response.headers.clone(); - if !self.options.disable_masking { + if !options.disable_masking { for (_key, value) in &mut headers { if let Some(value_str) = value.to_str().ok() { let masked_value = mask_json( @@ -266,7 +253,7 @@ impl Executor { } } - if self.options.raw_output { + if options.raw_output { for (key, value) in headers { println!( "{}: {}", @@ -290,16 +277,16 @@ impl Executor { } } - if !self.options.hide_body { + if !options.hide_body { let mut body = serde_json::from_str::(&response.text) .map_err(|error| ExecutionError::Unknown(error.to_string()))?; - if !self.options.disable_masking { + if !options.disable_masking { body = mask_json(body, &response.request.masking_rules) .map_err(|error| ExecutionError::Unknown(error.to_string()))?; } - if self.options.raw_output { + if options.raw_output { println!( "{}", serde_json::to_string(&body) diff --git a/src/main.rs b/src/main.rs index 061e9c2..088ef76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,57 @@ +mod cli; mod executor; mod logging; mod masking; -mod options; mod request; mod resolvers; mod response; + use clap::Parser; +use cli::{Cli, CollectionCommands, Commands, RequestCommands}; use executor::Executor; use logging::init_logging; -use options::Options; #[tokio::main] async fn main() -> Result<(), Box> { let guard = init_logging()?; - let options = Options::parse(); + match Cli::parse().command { + Commands::Collection { command } => match command { + CollectionCommands::Run { + collection, + output_options, + } => { + let requests = request::load_requests_from_toml(collection.as_str())?; + + let cloned_requests: Vec<_> = requests.clone(); + + let mut executor = Executor::new(requests); + + for request in cloned_requests { + let response = executor.execute_request(request.clone()).await?; + + executor + .render_output(response, output_options.clone()) + .await?; + } + } + }, + Commands::Request { command } => match command { + RequestCommands::Run { + collection, + request, + output_options, + } => { + let requests = request::load_requests_from_toml(collection.as_str())?; + + let mut executor = Executor::new(requests); - let requests = request::load_requests_from_toml(options.collection.as_str())?; + let response = executor.execute_request_named(request).await?; - Executor::new(requests, options).execute().await?; + executor.render_output(response, output_options).await?; + } + }, + } drop(guard); From bdc5ffd44c6a3e0283f722492bf58ad4bd2b3166 Mon Sep 17 00:00:00 2001 From: John Freeman <371481+johnnyfreeman@users.noreply.github.com> Date: Mon, 23 Dec 2024 02:44:07 -0500 Subject: [PATCH 3/4] removed old cli --- src/options.rs | 31 ------------------------------- 1 file changed, 31 deletions(-) delete mode 100644 src/options.rs diff --git a/src/options.rs b/src/options.rs deleted file mode 100644 index b532765..0000000 --- a/src/options.rs +++ /dev/null @@ -1,31 +0,0 @@ -use clap::Parser; - -#[derive(Debug, Parser)] -#[command(version, about, long_about = None)] -pub struct Options { - /// The collection file to use - pub collection: String, - - /// The specific request to execute within the collection (optional) - pub request: Option, - - /// Displays the HTTP headers in the output (disabled by default) - #[arg(short = 'h', long, default_value_t = false)] - pub show_headers: bool, - - /// Suppresses the HTTP response status (enabled by default) - #[arg(short = 's', long, default_value_t = false)] - pub hide_status: bool, - - /// Suppresses the HTTP response body (enabled by default) - #[arg(short = 'b', long, default_value_t = false)] - pub hide_body: bool, - - /// Disables pretty-printing for the HTTP response (enabled by default) - #[arg(short = 'r', long, default_value_t = false)] - pub raw_output: bool, - - /// Disables pre-output masking (enabled by default) - #[arg(short = 'm', long, default_value_t = false)] - pub disable_masking: bool, -} From 718a92b6dfa1af4ea6a3faaed7f3676892be6fd9 Mon Sep 17 00:00:00 2001 From: John Freeman <371481+johnnyfreeman@users.noreply.github.com> Date: Sun, 29 Dec 2024 15:22:26 -0500 Subject: [PATCH 4/4] cleanup --- src/cli.rs | 40 ++++++++++++++++++++++++---------------- src/main.rs | 4 ++-- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 5d95918..6f05966 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,73 +1,81 @@ use clap::{Args, Parser, Subcommand}; +/// A CLI application for managing and executing HTTP requests or collections. #[derive(Parser)] #[command(version, about, long_about = None)] pub struct Cli { + /// The primary command to execute. #[command(subcommand)] pub command: Commands, } +/// Top-level commands for the CLI. #[derive(Subcommand)] pub enum Commands { - /// does testing things - Collection { + /// Work with request collections (grouped sets of HTTP requests). + Collections { + /// Subcommands related to request collections. #[command(subcommand)] command: CollectionCommands, }, - /// does testing things - Request { + /// Work with individual HTTP requests. + Requests { + /// Subcommands related to individual HTTP requests. #[command(subcommand)] command: RequestCommands, }, } +/// Subcommands for managing and executing request collections. #[derive(Subcommand)] pub enum CollectionCommands { - /// Does testing things + /// Execute an entire collection of requests. Run { - /// The collection file to use + /// The collection file containing the requests to execute. collection: String, - /// Additional output options + /// Additional options for controlling output behavior. #[command(flatten)] output_options: OutputOptions, }, } +/// Additional output options for fine-tuning the CLI's behavior. #[derive(Args, Clone)] pub struct OutputOptions { - /// Displays the HTTP headers in the output (disabled by default) + /// Displays the HTTP headers in the output (disabled by default). #[arg(short = 'h', long, default_value_t = false)] pub show_headers: bool, - /// Suppresses the HTTP response status (enabled by default) + /// Suppresses the HTTP response status (enabled by default). #[arg(short = 's', long, default_value_t = false)] pub hide_status: bool, - /// Suppresses the HTTP response body (enabled by default) + /// Suppresses the HTTP response body (enabled by default). #[arg(short = 'b', long, default_value_t = false)] pub hide_body: bool, - /// Disables pretty-printing for the HTTP response (enabled by default) + /// Disables pretty-printing for the HTTP response (enabled by default). #[arg(short = 'r', long, default_value_t = false)] pub raw_output: bool, - /// Disables pre-output masking (enabled by default) + /// Disables pre-output masking (enabled by default). #[arg(short = 'm', long, default_value_t = false)] pub disable_masking: bool, } +/// Subcommands for managing and executing individual requests. #[derive(Subcommand)] pub enum RequestCommands { - /// does testing things + /// Execute a specific request within a collection. Run { - /// The collection file to use + /// The collection file containing the requests. collection: String, - /// The specific request to execute within the collection + /// The specific request to execute within the collection. request: String, - /// Additional output options + /// Additional options for controlling output behavior. #[command(flatten)] output_options: OutputOptions, }, diff --git a/src/main.rs b/src/main.rs index 088ef76..24c2ae8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ async fn main() -> Result<(), Box> { let guard = init_logging()?; match Cli::parse().command { - Commands::Collection { command } => match command { + Commands::Collections { command } => match command { CollectionCommands::Run { collection, output_options, @@ -36,7 +36,7 @@ async fn main() -> Result<(), Box> { } } }, - Commands::Request { command } => match command { + Commands::Requests { command } => match command { RequestCommands::Run { collection, request,