service_kit 0.1.2

A foundational toolkit for building high-performance, modular services in Rust.
Documentation
#[cfg(not(target_arch = "wasm32"))]
use crate::error::{Error, Result};
#[cfg(not(target_arch = "wasm32"))]
use clap::ArgMatches;
#[cfg(not(target_arch = "wasm32"))]
use oas::OpenAPIV3;
#[cfg(not(target_arch = "wasm32"))]
use reqwest::Client;
#[cfg(not(target_arch = "wasm32"))]
use serde_json::Value;
#[cfg(not(target_arch = "wasm32"))]
use std::collections::HashMap;

#[cfg(not(target_arch = "wasm32"))]
pub async fn fetch_openapi_spec(base_url: &str) -> Result<OpenAPIV3> {
    let spec_url = format!("{}/api-docs/openapi.json", base_url.trim_end_matches('/'));
    println!("--> Fetching OpenAPI spec from: {}", spec_url);

    let response = reqwest::get(&spec_url).await?;
    if !response.status().is_success() {
        return Err(Error::SpecError(format!(
            "Failed to fetch spec, status: {}",
            response.status()
        )));
    }

    let spec: OpenAPIV3 = response.json().await?;
    Ok(spec)
}

#[cfg(not(target_arch = "wasm32"))]
pub async fn execute_request(
    base_url: &str,
    subcommand_name: &str,
    matches: &ArgMatches,
    spec: &OpenAPIV3,
) -> Result<()> {
    let client = Client::new();

    let parts: Vec<&str> = subcommand_name.split('.').collect();
    let method_str = parts.last().unwrap().to_uppercase();
    let path_template = format!("/{}", parts[..parts.len() - 1].join("/"));

    let path_item = spec
        .paths
        .get(&path_template)
        .ok_or_else(|| Error::SpecError(format!("Path not found for {}", path_template)))?;

    let operation = match method_str.as_str() {
        "GET" => path_item.get.as_ref(),
        "POST" => path_item.post.as_ref(),
        "PUT" => path_item.put.as_ref(),
        "DELETE" => path_item.delete.as_ref(),
        "PATCH" => path_item.patch.as_ref(),
        _ => None,
    }
    .ok_or_else(|| Error::SpecError(format!("Operation not found for {}", subcommand_name)))?;

    let mut final_path = path_template.clone();
    let mut query_params = HashMap::new();

    if let Some(params) = &operation.parameters {
        for param_ref in params {
            if let oas::Referenceable::Data(param) = param_ref {
                if let Some(value) = matches.get_one::<String>(&param.name) {
                    match param._in {
                        oas::ParameterIn::Path => {
                            final_path = final_path.replace(&format!("{{{}}}", param.name), value);
                        }
                        oas::ParameterIn::Query => {
                            query_params.insert(param.name.clone(), value.clone());
                        }
                        _ => {}
                    }
                }
            }
        }
    }
    
    let mut request_url = format!("{}{}", base_url, final_path);
    if !query_params.is_empty() {
        let query_string = serde_urlencoded::to_string(query_params).unwrap();
        request_url.push('?');
        request_url.push_str(&query_string);
    }

    println!("--> Making {} request to: {}", method_str, request_url);

    let mut request_builder = match method_str.as_str() {
        "GET" => client.get(&request_url),
        "POST" => client.post(&request_url),
        "PUT" => client.put(&request_url),
        "DELETE" => client.delete(&request_url),
        "PATCH" => client.patch(&request_url),
        _ => return Err(Error::SpecError(format!("Unsupported method {}", method_str))),
    };

    if let Some(oas::Referenceable::Data(request_body)) = &operation.request_body {
        if request_body.content.contains_key("application/json") {
            if let Some(body_str) = matches.get_one::<String>("body") {
                let json_body: Value = serde_json::from_str(body_str)?;
                request_builder = request_builder.json(&json_body);
            }
        }
    }

    let response = request_builder.send().await?;
    let status = response.status();
    println!("<-- Response Status: {}", status);

    let response_body = response.text().await?;
    if let Ok(json_body) = serde_json::from_str::<Value>(&response_body) {
        println!("{}", serde_json::to_string_pretty(&json_body)?);
    } else {
        println!("{}", response_body);
    }

    Ok(())
}