hen 0.8.0

Run API collections from the command line.
use clap::{self, Parser};
use simple_logger;
use std::path::PathBuf;

use prompt_generator::{collection_prompt, request_prompt, scan_directory};

mod collection;
mod parser;
mod prompt_generator;
mod request;
mod benchmark;

// hen -> prompt for a collection in CWD
// hen <dir> -> prompt for a collections in a specific directory
// hen <file> -> prompt to run a specific request
// hen <file> --all -> run all requests
#[derive(clap::Parser, Debug)]
#[command(name = "hen")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "Command line API client.")]
struct Cli {
    path: Option<String>,
    selector: Option<String>,

    #[arg(long)]
    export: bool,

    #[arg(long)]
    benchmark: Option<usize>,

    #[arg(short = 'v', long)]
    verbose: bool,
}

#[tokio::main]
async fn main() {
    let args: Cli = Cli::parse();

    if args.verbose {
        simple_logger::init_with_level(log::Level::Debug).expect("Failed to initialize logger.");
    }

    log::debug!("Starting hen with args {:?}", args);

    let cwd = std::env::current_dir().unwrap();

    // get the collection to load, either
    // 1. directly from, if supplied a filepath
    // 2. or via a prompt if supplied a directory
    let collection = match args.path {
        Some(path) => {
            // is the path a directory or a file?
            let path = PathBuf::from(path);
            if path.is_dir() {
                // if there is only one valid file, load it automatically and skip the prompt
                let hen_files = scan_directory(path.clone());
                if hen_files.len() == 1 {
                    collection::Collection::new(hen_files.get(0).unwrap().clone())
                } else {
                    // prompt for a collection in the directory
                    collection_prompt(path)
                }
            } else {
                // prompt for a request in the file
                collection::Collection::new(path)
            }
        }

        None => match collection_prompt(cwd) {
            collection => collection,
        },
    };

    let collection = match collection {
        Ok(collection) => collection,
        Err(e) => {
            eprintln!("Error: {}", e);
            return;
        }
    };

    log::debug!("PARSED COLLECTION\n{:#?}", collection);

    // get the requests to execute
    // this is a vector to allow for the possibility of running all requests.
    // if a selector is provided, it is used to select a request instead of prompting the user.
    // Else, the user is prompted to select a request if there is more than one in a collection.
    // if there is only one request, it is automatically selected and the prompt is skipped.
    let requests = match args.selector {
        Some(selector) => {
            match selector {
                // return all requests
                ref s if s == "all" => collection.requests,
                // return a specific request
                _ => {
                    // parse the selector as an integer
                    let index = selector.parse::<usize>().unwrap();

                    // return the request at the index
                    let index_request = collection.requests.get(index);

                    match index_request {
                        Some(request) => vec![request.clone()],
                        None => {
                            eprintln!("Request not found: {}", selector);
                            return;
                        }
                    }
                }
            }
        }
        None => {
            if collection.requests.len() == 1 {
                vec![collection.requests[0].clone()]
            } else {
                vec![request_prompt(collection)]
            }
        }
    };

    // exec array of requests
    for req in requests {

        if args.export {
            println!("{}", req.as_curl());
            continue;
        }

        if let Some(count) = args.benchmark {
            benchmark::benchmark(req.clone(), count).await;
            continue;
        }

        let response = req.exec().await.unwrap();
        println!("{}", response);
    }
}