catalyst 0.2.9

A lightweight API testing tool
Documentation
use crate::models::config::Config;
use reqwest::Client;
use serde_json::Value;
use std::collections::HashMap;

pub struct RequestData {
    pub method: String,
    pub url: String,
    pub headers: Vec<(String, String)>,
    pub params: Vec<(String, String)>,
    pub body: Option<Value>,
}

pub struct HttpClient {
    client: Client,
    config: Config,
}

impl HttpClient {
    pub fn new(config: &Config) -> Self {
        let client = if config.insecure.unwrap_or(false) {
            Client::builder()
                .danger_accept_invalid_certs(true)
                .build()
                .expect("Failed to build HTTP client")
        } else {
            Client::new()
        };
        
        HttpClient {
            client,
            config: config.clone(),
        }
    }

    pub async fn execute(
        &self,
        request: RequestData,
    ) -> Result<(u16, Value, HashMap<String, String>), String> {
        let url = format!("{}{}", self.config.base_url, request.url);

        let mut builder = self
            .client
            .request(request.method.parse().unwrap(), url)
            .query(&request.params);

        if let Some(default_headers) = &self.config.default_headers {
            for (k, v) in default_headers {
                builder = builder.header(k, v);
            }
        }

        for (k, v) in request.headers {
            builder = builder.header(k, v);
        }

        if let Some(body) = request.body {
            builder = builder.json(&body);
        }

        let response = builder.send().await.map_err(|e| e.to_string())?;
        let status = response.status().as_u16();

        let headers: HashMap<String, String> = response
            .headers()
            .iter()
            .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or_default().to_string()))
            .collect();

        let body = if response.content_length().unwrap_or(0) == 0 {
            Value::Null
        } else {
            match response.text().await {
                Ok(text) => match serde_json::from_str(&text) {
                    Ok(json) => json,
                    Err(_) => Value::String(text),
                },
                Err(_) => Value::String("Failed to read response body".into()),
            }
        };

        Ok((status, body, headers))
    }
}