Skip to content

Commit

Permalink
feat: displaying status time and size of requests
Browse files Browse the repository at this point in the history
  • Loading branch information
wllfaria committed May 27, 2024
1 parent 56f6977 commit 1c80b48
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 37 deletions.
2 changes: 1 addition & 1 deletion config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod config;
mod data;
pub mod data;
mod default_config;

pub use config::{
Expand Down
8 changes: 6 additions & 2 deletions reqtui/src/net/request_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ use tokio::sync::mpsc::UnboundedSender;

#[derive(Debug, PartialEq)]
pub struct Response {
pub body: String,
pub pretty_body: TextObject<Readonly>,
pub body: Option<String>,
pub pretty_body: Option<TextObject<Readonly>>,
pub headers: HeaderMap<HeaderValue>,
pub duration: Duration,
pub status: reqwest::StatusCode,
pub headers_size: u64,
pub body_size: u64,
pub size: u64,
}

pub struct RequestManager;
Expand Down
8 changes: 7 additions & 1 deletion reqtui/src/net/request_strategies/http_strategy.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::error::Error;

use crate::{
collection::types::{Request, RequestMethod},
net::{
Expand Down Expand Up @@ -36,7 +38,11 @@ impl HttpResponse {
let decoder = decoder_from_headers(response.headers());
decoder.decode(response, now).await
}
Err(_) => todo!(),
Err(e) => {
tracing::error!("{:?}", e.source());
tracing::debug!("{e:?}");
todo!();
}
}
}

Expand Down
26 changes: 21 additions & 5 deletions reqtui/src/net/response_decoders/json_decoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
net::{request_manager::Response, response_decoders::ResponseDecoder},
text_object::TextObject,
};
use std::time::Instant;
use std::{ops::Add, time::Instant};

pub struct JsonDecoder;

Expand All @@ -15,16 +15,32 @@ impl ResponseDecoder for JsonDecoder {
) -> anyhow::Result<Response> {
let duration = start.elapsed();
let headers = response.headers().to_owned();
let body: serde_json::Value = response.json().await?;
let pretty_body = serde_json::to_string_pretty(&body)?;
let pretty_body = TextObject::from(&pretty_body);
let body = body.to_string();
let status = response.status();
let headers_size: u64 = response
.headers()
.iter()
.map(|(k, v)| k.as_str().len().add(v.as_bytes().len()).add(4) as u64)
.sum();

let mut body: Option<String> = None;
let mut pretty_body = None;
if response.content_length().is_some_and(|len| len.gt(&0)) {
body = Some(response.json().await?);
pretty_body = Some(TextObject::from(&serde_json::to_string_pretty(&body)?));
}

let body_size = body.as_ref().map(|body| body.len()).unwrap_or_default() as u64;
let size = headers_size.add(body_size);

Ok(Response {
body,
pretty_body,
headers,
duration,
status,
size,
headers_size,
body_size,
})
}
}
120 changes: 92 additions & 28 deletions tui/src/pages/collection_viewer/res_viewer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ratatui::{
buffer::Buffer,
layout::{Constraint, Direction, Layout, Rect},
style::{Style, Stylize},
text::Line,
text::{Line, Span},
widgets::{
Block, Borders, Padding, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState,
StatefulWidget, Tabs, Widget,
Expand Down Expand Up @@ -62,6 +62,7 @@ impl From<ResViewerTabs> for usize {
pub struct ResViewerLayout {
tabs_pane: Rect,
content_pane: Rect,
summary_pane: Rect,
}

impl<'a> ResViewerState<'a> {
Expand Down Expand Up @@ -97,9 +98,13 @@ pub struct ResViewer<'a> {
impl<'a> ResViewer<'a> {
pub fn new(colors: &'a colors::Colors, response: Option<Rc<RefCell<Response>>>) -> Self {
let tree = response.as_ref().and_then(|response| {
let pretty_body = response.borrow().pretty_body.to_string();
let mut highlighter = HIGHLIGHTER.write().unwrap();
highlighter.parse(&pretty_body)
if let Some(ref pretty_body) = response.borrow().pretty_body {
let pretty_body = pretty_body.to_string();
let mut highlighter = HIGHLIGHTER.write().unwrap();
highlighter.parse(&pretty_body)
} else {
None
}
});

ResViewer {
Expand All @@ -111,16 +116,22 @@ impl<'a> ResViewer<'a> {
}

pub fn update(&mut self, response: Option<Rc<RefCell<Response>>>) {
self.tree = response.as_ref().and_then(|response| {
let pretty_body = response.borrow().pretty_body.to_string();
let mut highlighter = HIGHLIGHTER.write().unwrap();
highlighter.parse(&pretty_body)
});

if let Some(ref res) = response {
let pretty_body = res.borrow().pretty_body.to_string();
self.lines =
build_syntax_highlighted_lines(&pretty_body, self.tree.as_ref(), self.colors);
let body_str = response
.as_ref()
.and_then(|res| {
res.borrow()
.pretty_body
.as_ref()
.map(|body| body.to_string())
})
.unwrap_or_default();

if body_str.len().gt(&0) {
self.tree = HIGHLIGHTER.write().unwrap().parse(&body_str);
self.lines = build_syntax_highlighted_lines(&body_str, self.tree.as_ref(), self.colors);
} else {
self.tree = None;
self.lines = vec![];
}

self.response = response;
Expand Down Expand Up @@ -246,16 +257,21 @@ impl<'a> ResViewer<'a> {

fn draw_raw_response(&self, state: &mut ResViewerState, buf: &mut Buffer, size: Rect) {
if let Some(response) = self.response.as_ref() {
let lines = response
.borrow()
.body
.chars()
.collect::<Vec<_>>()
// accounting for the scrollbar width when splitting the lines
.chunks(size.width.saturating_sub(2).into())
.map(|row| Line::from(row.iter().collect::<String>()))
.collect::<Vec<_>>();

let lines = if response.borrow().body.is_some() {
response
.borrow()
.body
.as_ref()
.unwrap()
.chars()
.collect::<Vec<_>>()
// accounting for the scrollbar width when splitting the lines
.chunks(size.width.saturating_sub(2).into())
.map(|row| Line::from(row.iter().collect::<String>()))
.collect::<Vec<_>>()
} else {
vec![Line::from("No body").centered()]
};
// allow for scrolling down until theres only one line left into view
if state.raw_scroll.deref().ge(&lines.len().saturating_sub(1)) {
*state.raw_scroll = lines.len().saturating_sub(1);
Expand Down Expand Up @@ -325,9 +341,13 @@ impl<'a> ResViewer<'a> {

self.draw_scrollbar(self.lines.len(), *state.raw_scroll, buf, scrollbar_pane);

let lines_in_view = self
.lines
.clone()
let lines = if self.lines.len().gt(&0) {
self.lines.clone()
} else {
vec![Line::from("No body").centered()]
};

let lines_in_view = lines
.into_iter()
.skip(*state.pretty_scroll)
.chain(iter::repeat(Line::from("~".fg(self.colors.bright.black))))
Expand All @@ -338,6 +358,47 @@ impl<'a> ResViewer<'a> {
pretty_response.render(request_pane, buf);
}
}

fn draw_summary(&self, buf: &mut Buffer, size: Rect) {
if let Some(ref response) = self.response {
let status_color = match response.borrow().status.as_u16() {
s if s < 400 => self.colors.normal.green,
_ => self.colors.normal.red,
};
let status = if size.width.gt(&50) {
format!(
"{} ({})",
response.borrow().status.as_str(),
response
.borrow()
.status
.canonical_reason()
.expect("tried to get a canonical_reason from a invalid status code")
)
.fg(status_color)
} else {
response
.borrow()
.status
.as_str()
.to_string()
.fg(status_color)
};
let pieces: Vec<Span> = vec![
"Status: ".fg(self.colors.bright.black),
status,
" ".into(),
"Time: ".fg(self.colors.bright.black),
format!("{}ms", response.borrow().duration.as_millis())
.fg(self.colors.normal.green),
" ".into(),
"Size: ".fg(self.colors.bright.black),
format!("{} B", response.borrow().size).fg(self.colors.normal.green),
];

Line::from(pieces).render(size, buf);
}
}
}

impl<'a> StatefulWidget for ResViewer<'a> {
Expand All @@ -349,6 +410,7 @@ impl<'a> StatefulWidget for ResViewer<'a> {
self.draw_container(size, buf, state);
self.draw_tabs(buf, state, layout.tabs_pane);
self.draw_current_tab(state, buf, layout.content_pane);
self.draw_summary(buf, layout.summary_pane);
}
}

Expand All @@ -360,18 +422,20 @@ fn build_layout(size: Rect) -> ResViewerLayout {
size.height.saturating_sub(2),
);

let [tabs_pane, _, content_pane] = Layout::default()
let [tabs_pane, _, content_pane, summary_pane] = Layout::default()
.constraints([
Constraint::Length(1),
Constraint::Length(1),
Constraint::Fill(1),
Constraint::Length(1),
])
.direction(Direction::Vertical)
.areas(size);

ResViewerLayout {
tabs_pane,
content_pane,
summary_pane,
}
}

Expand Down

0 comments on commit 1c80b48

Please sign in to comment.