coman/cli/
request.rs

1//! CLI commands for making HTTP requests
2//!
3//! This module provides the command-line interface for making HTTP requests,
4//! including progress bars, colored output, and interactive prompts.
5
6use crate::cli::request_data::RequestData;
7use crate::HttpResponse;
8use clap::Subcommand;
9use colored::{ColoredString, Colorize};
10use serde_json::Value;
11use std::fmt;
12use std::io::{self, Write};
13
14#[derive(Subcommand, Clone, Debug)]
15pub enum RequestCommands {
16    Get {
17        #[clap(flatten)]
18        data: RequestData,
19    },
20    Post {
21        #[clap(flatten)]
22        data: RequestData,
23    },
24    Put {
25        #[clap(flatten)]
26        data: RequestData,
27    },
28    Delete {
29        #[clap(flatten)]
30        data: RequestData,
31    },
32    Patch {
33        #[clap(flatten)]
34        data: RequestData,
35    },
36}
37
38impl fmt::Display for RequestCommands {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Self::Get { .. } => write!(f, "GET"),
42            Self::Post { .. } => write!(f, "POST"),
43            Self::Put { .. } => write!(f, "PUT"),
44            Self::Delete { .. } => write!(f, "DELETE"),
45            Self::Patch { .. } => write!(f, "PATCH"),
46        }
47    }
48}
49
50impl RequestCommands {
51    pub fn get_data(&self) -> &RequestData {
52        // assuming RequestData is the type of 'data'
53        match self {
54            Self::Get { data }
55            | Self::Post { data }
56            | Self::Put { data }
57            | Self::Delete { data }
58            | Self::Patch { data } => data,
59        }
60    }
61
62    pub fn print_request_method(&self, url: &str, status: u16, elapsed: u128) {
63        println!(
64            "\n[{}] {} - {} ({} ms)\n",
65            self.to_string().bold().bright_yellow(),
66            url.to_string().bold().bright_white(),
67            Self::colorize_status(status.to_string().parse().unwrap()),
68            elapsed
69        );
70    }
71
72    pub fn print_request_headers(headers: &[(String, String)]) {
73        println!("{}", "Request Headers:".to_string().bold().bright_blue());
74        for (key, value) in headers.iter() {
75            println!("  {}: {:?}", key.to_string().bright_white(), value);
76        }
77    }
78
79    pub fn print_request_body(body: &str) {
80        println!("{}", "Request Body:".to_string().bold().bright_blue());
81        println!("{}", body.italic());
82    }
83
84    pub async fn print_request_response(
85        response: &HttpResponse,
86        verbose: bool,
87        stream: bool,
88    ) -> Result<(), Box<dyn std::error::Error>> {
89        if verbose && !stream {
90            println!("{}", "Response Headers:".to_string().bold().bright_blue());
91            for (key, value) in response.headers.iter() {
92                println!("  {}: {:?}", key.to_string().bright_white(), value);
93            }
94            println!("\n{}", "Response Body:".to_string().bold().bright_blue());
95        }
96
97        if !stream {
98            //Try parsing the body as JSON
99            if let Ok(json) = response.json::<Value>() {
100                let pretty = serde_json::to_string_pretty(&json)?;
101                println!("{}", pretty.green());
102            } else {
103                println!("{}", response.body.italic());
104            }
105        }
106
107        Ok(())
108    }
109
110    pub fn colorize_status(status: u16) -> ColoredString {
111        match status {
112            200..=299 => status.to_string().bold().bright_green(),
113            300..=499 => status.to_string().bold().bright_yellow(),
114            500..=599 => status.to_string().bold().bright_red(),
115            _ => status.to_string().white(),
116        }
117    }
118
119    pub fn prompt_missing_header_data(mut headers: Vec<(String, String)>) -> Vec<(String, String)> {
120        for header in headers.iter_mut() {
121            if header.1.contains(":?") {
122                eprint!(
123                    "Header value for key '{}' is missing data. Please provide the correct value: ",
124                    header.0
125                );
126                io::stdout().flush().ok();
127                let mut new_value = String::new();
128                std::io::stdin()
129                    .read_line(&mut new_value)
130                    .expect("Failed to read header value");
131                header.1 = new_value.trim().to_string();
132            }
133        }
134        headers
135    }
136
137    pub fn prompt_missing_body_data(mut body: String) -> String {
138        while let Some(idx) = body.find(":?") {
139            eprint!(
140                "Missing data at position {} - {}. Please provide the correct value: ",
141                idx, body
142            );
143            io::stdout().flush().ok();
144            let mut replacement = String::new();
145            std::io::stdin()
146                .read_line(&mut replacement)
147                .expect("Failed to read body placeholder");
148            let replacement = replacement.trim();
149            body.replace_range(idx..idx + 2, replacement);
150        }
151        body
152    }
153
154    /// Checks if the Vec<u8> is valid UTF-8 (likely text) or not (binary).
155    pub fn is_text_data(data: &[u8]) -> bool {
156        std::str::from_utf8(data).is_ok()
157    }
158}