1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! HTTP response types
use std::collections::HashMap;
use std::time::Duration;
use crate::style::Color;
use super::types::ContentType;
/// HTTP response
#[derive(Clone, Debug, Default)]
pub struct HttpResponse {
/// Status code
pub status: u16,
/// Status text
pub status_text: String,
/// Response headers
pub headers: HashMap<String, String>,
/// Response body
pub body: String,
/// Response time
pub time: Duration,
/// Response size in bytes
pub size: usize,
}
impl HttpResponse {
/// Check if status is success (2xx)
pub fn is_success(&self) -> bool {
(200..300).contains(&self.status)
}
/// Get status color
pub fn status_color(&self) -> Color {
match self.status {
200..=299 => Color::rgb(152, 195, 121), // Green
300..=399 => Color::rgb(229, 192, 123), // Yellow
400..=499 => Color::rgb(224, 108, 117), // Red
500..=599 => Color::rgb(198, 120, 221), // Purple
_ => Color::rgb(171, 178, 191), // Gray
}
}
/// Get content type
pub fn content_type(&self) -> ContentType {
ContentType::from_header(self.headers.get("Content-Type").map(|s| s.as_str()))
}
/// Try to format body as pretty JSON
pub fn pretty_json(&self) -> Option<String> {
self.format_json(&self.body)
}
/// Format JSON string with indentation
pub fn format_json(&self, json: &str) -> Option<String> {
// Simple JSON formatter without external dependencies
let mut result = String::new();
let mut indent = 0usize;
let mut in_string = false;
let mut escape_next = false;
for ch in json.chars() {
if escape_next {
result.push(ch);
escape_next = false;
continue;
}
if ch == '\\' && in_string {
result.push(ch);
escape_next = true;
continue;
}
if ch == '"' {
in_string = !in_string;
result.push(ch);
continue;
}
if in_string {
result.push(ch);
continue;
}
match ch {
'{' | '[' => {
result.push(ch);
indent += 1;
result.push('\n');
result.push_str(&" ".repeat(indent));
}
'}' | ']' => {
indent = indent.saturating_sub(1);
result.push('\n');
result.push_str(&" ".repeat(indent));
result.push(ch);
}
',' => {
result.push(ch);
result.push('\n');
result.push_str(&" ".repeat(indent));
}
':' => {
result.push_str(": ");
}
' ' | '\n' | '\r' | '\t' => {
// Skip whitespace outside strings
}
_ => {
result.push(ch);
}
}
}
if result.is_empty() {
None
} else {
Some(result)
}
}
/// Get formatted body based on content type
pub fn formatted_body(&self) -> String {
match self.content_type() {
ContentType::Json => self.pretty_json().unwrap_or_else(|| self.body.clone()),
_ => self.body.clone(),
}
}
}