diff --git a/src/app/app_logic/request/send.rs b/src/app/app_logic/request/send.rs index ab9d151..70a295c 100644 --- a/src/app/app_logic/request/send.rs +++ b/src/app/app_logic/request/send.rs @@ -11,6 +11,7 @@ use reqwest::redirect::Policy; use tokio::task; use crate::app::app::App; +use crate::panic_error; use crate::request::auth::Auth::{BasicAuth, BearerToken, NoAuth}; use crate::request::body::{ContentType, find_file_format_in_content_type}; @@ -51,7 +52,10 @@ impl App<'_> { match &proxy.http_proxy { None => {} Some(http_proxy_str) => { - let proxy = Proxy::http(http_proxy_str).expect("Could not parse HTTP proxy"); + let proxy = match Proxy::http(http_proxy_str) { + Ok(proxy) => proxy, + Err(e) => panic_error(format!("Could not parse HTTP proxy\n\t{e}")) + }; client_builder = client_builder.proxy(proxy); } } @@ -59,7 +63,10 @@ impl App<'_> { match &proxy.https_proxy { None => {} Some(https_proxy_str) => { - let proxy = Proxy::https(https_proxy_str).expect("Could not parse HTTPS proxy"); + let proxy = match Proxy::https(https_proxy_str) { + Ok(proxy) => proxy, + Err(e) => panic_error(format!("Could not parse HTTPS proxy\n\t{e}")) + }; client_builder = client_builder.proxy(proxy); } } diff --git a/src/app/files/collection.rs b/src/app/files/collection.rs index 6b60040..a0a95b9 100644 --- a/src/app/files/collection.rs +++ b/src/app/files/collection.rs @@ -2,8 +2,10 @@ use std::fs; use std::fs::OpenOptions; use std::io::{Read, Write}; use std::path::PathBuf; + use crate::app::app::App; use crate::app::startup::args::ARGS; +use crate::panic_error; use crate::request::collection::Collection; impl App<'_> { @@ -20,32 +22,21 @@ impl App<'_> { collection_file.read_to_string(&mut file_content).expect("\tCould not read collection file"); + let mut collection: Collection = match serde_json::from_str(&file_content) { + Ok(collection) => collection, + Err(e) => panic_error(format!("Could not parse collection\n\t{e}")) + }; - if file_content.len() == 0 { - let collection = Collection { - name: path_buf.file_stem().unwrap().to_str().unwrap().to_string(), - requests: vec![], - path: path_buf, - }; - - let collection_json = serde_json::to_string_pretty(&collection).expect("Could not serialize collection"); - - collection_file.write_all(collection_json.as_bytes()).expect("Could not write to collection file") - } - else { - let mut collection: Collection = serde_json::from_str(&file_content).expect("\tCould not parse collection"); - - collection.path = path_buf; + collection.path = path_buf; - self.collections.push(collection); - } + self.collections.push(collection); println!("Collection file parsed!"); } /// Save app collection in the collection file through a temporary file pub fn save_collection_to_file(&mut self, collection_index: usize) { - if ARGS.dry_run { + if !ARGS.should_save { return; } @@ -72,7 +63,7 @@ impl App<'_> { /// Delete collection file pub fn delete_collection_file(&mut self, collection: Collection) { - if ARGS.dry_run { + if !ARGS.should_save { return; } diff --git a/src/app/files/config.rs b/src/app/files/config.rs index 91f69d9..5f7861b 100644 --- a/src/app/files/config.rs +++ b/src/app/files/config.rs @@ -1,8 +1,9 @@ use std::fs::OpenOptions; -use std::io::{Read, Write}; +use std::io::Read; use std::path::PathBuf; use serde::{Deserialize, Serialize}; use crate::app::app::App; +use crate::panic_error; #[derive(Default, Serialize, Deserialize)] pub struct Config { @@ -26,21 +27,17 @@ impl App<'_> { let mut config_file = OpenOptions::new() .read(true) .write(true) - .create(true) .open(path_buf.clone()) .expect("\tCould not open config file"); config_file.read_to_string(&mut file_content).expect("\tCould not read config file"); - if file_content.len() == 0 { - let config_toml = toml::to_string_pretty(&self.config).expect("\tCould not serialize config file"); - config_file.write_all(config_toml.as_bytes()).expect("\tCould not write to config file"); - } - else { - let config: Config = toml::from_str(&file_content).expect("\tCould not parse config file"); + let config: Config = match toml::from_str(&file_content) { + Ok(config) => config, + Err(e) => panic_error(format!("Could not parse config file\n\t{e}")) + }; - self.config = config - } + self.config = config; println!("Config file parsed!"); } diff --git a/src/app/files/environment.rs b/src/app/files/environment.rs index f5abf60..fe5af5e 100644 --- a/src/app/files/environment.rs +++ b/src/app/files/environment.rs @@ -1,13 +1,17 @@ use std::path::PathBuf; use envfile::EnvFile; use crate::app::app::App; +use crate::panic_error; use crate::request::environment::Environment; impl App<'_> { /// Add the environment file to the app environments pub fn add_environment_from_file(&mut self, path_buf: PathBuf) { let file_name = path_buf.file_name().unwrap().to_str().unwrap().to_string().replace(".env.", ""); - let env = EnvFile::new(path_buf).expect("\tCould not parse environment file"); + let env = match EnvFile::new(path_buf) { + Ok(env) => env, + Err(e) => panic_error(format!("Could not parse environment file\n\t{e}")) + }; let environment = Environment { name: file_name, diff --git a/src/app/files/log.rs b/src/app/files/log.rs index 427bd82..54bfd8e 100644 --- a/src/app/files/log.rs +++ b/src/app/files/log.rs @@ -3,15 +3,15 @@ use crate::app::app::App; impl App<'_> { pub fn write_to_log_file(&mut self, modifier: String, key: String, app_state: String) { - self.log_file - .as_ref() - .unwrap() - .write_fmt(format_args!( - "{:25}{:25}{:40}\n", - modifier, - key, - app_state, - )) - .expect("Could not write to log file"); + if let Some(log_file) = &mut self.log_file { + log_file + .write_fmt(format_args!( + "{:25}{:25}{:40}\n", + modifier, + key, + app_state, + )) + .expect("Could not write to log file") + } } } \ No newline at end of file diff --git a/src/app/files/postman.rs b/src/app/files/postman.rs index 8afdcdf..497223a 100644 --- a/src/app/files/postman.rs +++ b/src/app/files/postman.rs @@ -6,6 +6,7 @@ use parse_postman_collection::v2_1_0::{AuthType, Body, FormParameterSrcUnion, He use crate::app::app::App; use crate::app::startup::args::ARGS; +use crate::panic_error; use crate::request::auth::Auth; use crate::request::body::ContentType; use crate::request::collection::Collection; @@ -15,11 +16,16 @@ use crate::request::settings::RequestSettings; impl App<'_> { pub fn import_postman_collection(&mut self, path_buf: &PathBuf, max_depth: u16) { - let mut postman_collection = parse_postman_collection::from_path(path_buf).expect("\tCould not parse Postman collection"); + println!("Parsing Postman collection"); + + let mut postman_collection = match parse_postman_collection::from_path(path_buf) { + Ok(postman_collection) => postman_collection, + Err(e) => panic_error(format!("Could not parse Postman collection\n\t{e}")) + }; let collection_name = postman_collection.info.name.clone(); - println!("Parsing Postman collection \"{}\"", collection_name); + println!("Collection name: {}", collection_name); for existing_collection in &self.collections { if existing_collection.name == collection_name { @@ -191,7 +197,10 @@ fn parse_request(item: Items) -> Request { /* METHOD */ if let Some(method) = &request_class.method { - request.method = Method::from_str(method).expect(&format!("Unknown method \"{method}\"")); + request.method = match Method::from_str(method) { + Ok(method) => method, + Err(_) => panic_error(format!("Unknown method \"{method}\"")) + }; } /* AUTH */ diff --git a/src/app/startup/args.rs b/src/app/startup/args.rs index 96f6c51..2c48e62 100644 --- a/src/app/startup/args.rs +++ b/src/app/startup/args.rs @@ -1,13 +1,15 @@ +use std::env; use std::path::PathBuf; use clap::{Parser, Subcommand}; use lazy_static::lazy_static; +use crate::{panic_error}; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] pub struct Args { /// Main application directory, containing JSON collections files, the atac.toml config file and the atac.log file #[arg(short, long)] - pub directory: PathBuf, + pub directory: Option, #[command(subcommand)] pub command: Option, @@ -17,13 +19,13 @@ pub struct Args { pub dry_run: bool, } -#[derive(Debug, Subcommand)] +#[derive(Debug, Subcommand, PartialEq)] pub enum Command { /// Used to import a collection file such as Postman - Import(ImportArgs) + Import(ImportArgs), } -#[derive(Debug, clap::Args)] +#[derive(Debug, clap::Args, PartialEq)] pub struct ImportArgs { /// A file to import, only Postman v2.1 JSON collection for now pub path: PathBuf, @@ -33,6 +35,33 @@ pub struct ImportArgs { pub max_depth: Option, } +pub struct ParsedArgs { + pub directory: PathBuf, + pub is_directory_from_env: bool, + pub command: Option, + pub should_save: bool +} + lazy_static! { - pub static ref ARGS: Args = Args::parse(); + pub static ref ARGS: ParsedArgs = { + let args = Args::parse(); + + let (directory, is_directory_from_env) = match args.directory { + // If a directory was provided with a CLI argument + Some(arg_directory) => (arg_directory, false), + // If no directory was provided with the CLI + None => match env::var("ATAC_MAIN_DIR") { + // If the ATAC_MAIN_DIR environment variable exists + Ok(env_directory) => (PathBuf::from(env_directory), true), + Err(_) => panic_error("No directory provided, provide one either with `--directory ` or via the environment variable `ATAC_MAIN_DIR`") + } + }; + + ParsedArgs { + directory, + is_directory_from_env, + command: args.command, + should_save: !args.dry_run + } + }; } \ No newline at end of file diff --git a/src/app/startup/startup.rs b/src/app/startup/startup.rs index 2436f75..661a893 100644 --- a/src/app/startup/startup.rs +++ b/src/app/startup/startup.rs @@ -1,13 +1,19 @@ -use std::fs; use std::fs::OpenOptions; + use crate::app::app::App; use crate::app::startup::args::{ARGS, Command}; +use crate::panic_error; impl App<'_> { /// Method called before running the app pub fn startup(&mut self) -> &mut Self { self.parse_app_directory(); + // Creates the log file only if the app is allowed to save files + if ARGS.should_save { + self.create_log_file(); + } + if let Some(command) = &ARGS.command { match command { Command::Import(import_args) => { @@ -19,13 +25,11 @@ impl App<'_> { self } - pub fn parse_app_directory(&mut self) { - // Create the app directory if it does not exist - fs::create_dir_all(&ARGS.directory).expect(&format!("Could not create directory \"{}\"", ARGS.directory.display())); - - let paths = ARGS.directory.read_dir().expect(&format!("Directory \"{}\" not found", ARGS.directory.display())); - - let mut was_config_file_parsed = false; + fn parse_app_directory(&mut self) { + let paths = match ARGS.directory.read_dir() { + Ok(paths) => paths, + Err(e) => panic_error(format!("Directory \"{}\" not found\n\t{e}", ARGS.directory.display())) + }; for path in paths { let path = path.unwrap().path(); @@ -36,7 +40,7 @@ impl App<'_> { let file_name = path.file_name().unwrap().to_str().unwrap(); - println!("Parsing: {}", path.display()); + println!("Checking: {}", path.display()); if file_name.ends_with(".json") { self.set_collections_from_file(path); @@ -46,7 +50,6 @@ impl App<'_> { } else if file_name == "atac.toml" { self.parse_config_file(path); - was_config_file_parsed = true; } else if file_name == "atac.log" { println!("Nothing to parse here") @@ -54,18 +57,16 @@ impl App<'_> { println!(); } + } - self.log_file = Some( - OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(ARGS.directory.join("atac.log")) - .expect("Could not open log file") - ); - - if !was_config_file_parsed { - self.parse_config_file(ARGS.directory.join("atac.toml")); - } + fn create_log_file(&mut self) { + let path = ARGS.directory.join("atac.log"); + + let log_file = match OpenOptions::new().write(true).create(true).truncate(true).open(path) { + Ok(log_file) => log_file, + Err(e) => panic_error(format!("Could not open log file\n\t{e}")) + }; + + self.log_file = Some(log_file); } } diff --git a/src/main.rs b/src/main.rs index afb1e6f..a7ecb11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,19 +1,26 @@ -mod app; -mod request; -mod utils; +extern crate core; -use std::io::{stdout, Result}; -use crossterm::{ExecutableCommand}; +use std::fmt::Display; +use std::io::{Result, stdout}; +use std::process::exit; + +use crossterm::ExecutableCommand; +use crossterm::style::Stylize; use crossterm::terminal::{disable_raw_mode, LeaveAlternateScreen}; -pub use ratatui::backend::{Backend}; +pub use ratatui::backend::Backend; use ratatui::backend::CrosstermBackend; -use ratatui::{Terminal}; +use ratatui::Terminal; + use crate::app::app::App; +mod app; +mod request; +mod utils; + #[tokio::main] async fn main() -> Result<()> { let terminal = Terminal::new(CrosstermBackend::new(stdout()))?; - + App::new() .startup() .prepare_terminal() @@ -24,3 +31,8 @@ async fn main() -> Result<()> { disable_raw_mode()?; Ok(()) } + +pub fn panic_error(message: T) -> ! where T: Display { + println!("{error}:\n\t{message}", error = "Error".red().bold()); + exit(1); +} \ No newline at end of file