xs-http 0.1.0

Instant HTTP API testing suite - type-safe alternative to Postman/curl
Documentation
use colored::Colorize;
use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::parsing::SyntaxSet;
use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
use x_http::error::Result;
use x_http::Response;

pub fn display_response(response: &Response) -> Result<()> {
    println!("{}", "".repeat(80).bright_blue());
    println!("{} {}", "Status:".bold(), format_status(response.status()));
    println!("{} {:?}", "Duration:".bold(), response.duration());

    println!("\n{}", "Headers:".bold().cyan());
    for (key, value) in response.headers() {
        println!(
            "  {}: {}",
            key.as_str().green(),
            value.to_str().unwrap_or("<binary>")
        );
    }

    if let Ok(text) = response.text() {
        println!("\n{}", "Body:".bold().cyan());

        if let Some(content_type) = response.header("content-type") {
            if content_type.contains("application/json") {
                display_json(&text)?;
            } else {
                println!("{}", text);
            }
        } else {
            println!("{}", text);
        }
    } else {
        println!("\n{}", "Body: <binary data>".dimmed());
    }

    println!("{}", "".repeat(80).bright_blue());

    Ok(())
}

fn format_status(status: u16) -> String {
    let status_str = status.to_string();
    if (200..300).contains(&status) {
        status_str.green().to_string()
    } else if (300..400).contains(&status) {
        status_str.yellow().to_string()
    } else if status >= 400 {
        status_str.red().to_string()
    } else {
        status_str.white().to_string()
    }
}

fn display_json(json_text: &str) -> Result<()> {
    if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(json_text) {
        let formatted = serde_json::to_string_pretty(&parsed)?;

        if let Some(highlighted) = highlight_json(&formatted) {
            println!("{}", highlighted);
        } else {
            println!("{}", formatted);
        }
    } else {
        println!("{}", json_text);
    }

    Ok(())
}

fn highlight_json(json_text: &str) -> Option<String> {
    let ps = SyntaxSet::load_defaults_newlines();
    let ts = ThemeSet::load_defaults();

    let syntax = ps.find_syntax_by_extension("json")?;
    let theme = &ts.themes["base16-ocean.dark"];

    let mut highlighter = HighlightLines::new(syntax, theme);
    let mut output = String::new();

    for line in LinesWithEndings::from(json_text) {
        let ranges = highlighter.highlight_line(line, &ps).ok()?;
        let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
        output.push_str(&escaped);
    }

    Some(output)
}