xs-http 0.1.0

Instant HTTP API testing suite - type-safe alternative to Postman/curl
Documentation
use clap::{Parser, Subcommand};
use x_http::error::Result;

mod config;
mod display;
mod interactive;

use interactive::InteractiveSession;

#[derive(Parser)]
#[command(name = "x-http")]
#[command(version, about = "Instant HTTP API testing suite", long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Option<Commands>,
}

#[derive(Subcommand)]
enum Commands {
    Interactive,

    Run {
        #[arg(short, long, default_value = "x-http.toml")]
        config: String,

        #[arg(short, long)]
        name: Option<String>,
    },

    Request {
        method: String,

        url: String,

        #[arg(short = 'H', long)]
        header: Vec<String>,

        #[arg(short, long)]
        body: Option<String>,

        #[arg(short, long)]
        json: bool,
    },
}

fn main() -> Result<()> {
    let cli = Cli::parse();

    match cli.command {
        Some(Commands::Interactive) | None => {
            InteractiveSession::run()?;
        }
        Some(Commands::Run { config, name }) => {
            config::run_from_config(&config, name.as_deref())?;
        }
        Some(Commands::Request {
            method,
            url,
            header,
            body,
            json,
        }) => {
            quick_request(&method, &url, &header, body.as_deref(), json)?;
        }
    }

    Ok(())
}

fn quick_request(
    method: &str,
    url: &str,
    headers: &[String],
    body: Option<&str>,
    is_json: bool,
) -> Result<()> {
    use x_http::{Method, Request};

    let method = match method.to_uppercase().as_str() {
        "GET" => Method::Get,
        "POST" => Method::Post,
        "PUT" => Method::Put,
        "DELETE" => Method::Delete,
        "PATCH" => Method::Patch,
        "HEAD" => Method::Head,
        "OPTIONS" => Method::Options,
        _ => {
            eprintln!("Invalid method: {}", method);
            std::process::exit(1);
        }
    };

    let mut request = Request::new(method, url);

    for header in headers {
        if let Some((key, value)) = header.split_once(':') {
            request = request.header(key.trim(), value.trim());
        }
    }

    if let Some(body_str) = body {
        if is_json {
            let json_value: serde_json::Value = serde_json::from_str(body_str)?;
            request = request.json(&json_value)?;
        } else {
            request = request.text(body_str);
        }
    }

    let response = request.send()?;
    display::display_response(&response)?;

    Ok(())
}