diff --git a/hac-client/src/pages/collection_viewer/request_editor.rs b/hac-client/src/pages/collection_viewer/request_editor.rs index 4dc6077..33c3c24 100755 --- a/hac-client/src/pages/collection_viewer/request_editor.rs +++ b/hac-client/src/pages/collection_viewer/request_editor.rs @@ -2,7 +2,7 @@ mod auth_editor; mod body_editor; mod headers_editor; mod headers_editor_delete_prompt; -mod headers_editor_form; +mod headers_editor_edit_form; use auth_editor::AuthEditor; use body_editor::{BodyEditor, BodyEditorEvent}; diff --git a/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs b/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs index f36f752..3c427c4 100755 --- a/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs +++ b/hac-client/src/pages/collection_viewer/request_editor/headers_editor.rs @@ -7,7 +7,7 @@ use std::ops::{Div, Sub}; use std::{cell::RefCell, ops::Add, rc::Rc}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; -use hac_core::collection::header_map::HeaderMap; +use hac_core::collection::types::HeaderMap; use rand::Rng; use ratatui::layout::{Constraint, Direction, Layout, Rect}; use ratatui::style::{Style, Stylize}; @@ -18,7 +18,7 @@ use ratatui::Frame; use super::headers_editor_delete_prompt::{ HeadersEditorDeletePrompt, HeadersEditorDeletePromptEvent, }; -use super::headers_editor_form::{HeadersEditorForm, HeadersEditorFormEvent}; +use super::headers_editor_edit_form::{HeadersEditorForm, HeadersEditorFormEvent}; #[derive(Debug)] pub enum HeadersEditorEvent { @@ -337,7 +337,17 @@ impl Eventful for HeadersEditor<'_> { if let CollectionViewerOverlay::HeadersForm(_) = overlay { match self.header_form.handle_key_event(key_event)? { - Some(HeadersEditorFormEvent::FinishEdit) => {} + Some(HeadersEditorFormEvent::Quit) => { + return Ok(Some(HeadersEditorEvent::Quit)); + } + Some(HeadersEditorFormEvent::FinishEdit) => { + let mut store = self.collection_store.borrow_mut(); + store.pop_overlay(); + } + Some(HeadersEditorFormEvent::CancelEdit) => { + let mut store = self.collection_store.borrow_mut(); + store.pop_overlay(); + } None => {} } return Ok(None); @@ -420,6 +430,20 @@ impl Eventful for HeadersEditor<'_> { .borrow_mut() .push_overlay(CollectionViewerOverlay::HeadersForm(self.selected_row)); } + KeyCode::Char('n') => { + let idx = headers.len(); + headers.push(HeaderMap { + pair: Default::default(), + enabled: true, + }); + + self.selected_row = idx; + + drop(request); + self.collection_store + .borrow_mut() + .push_overlay(CollectionViewerOverlay::HeadersForm(idx)); + } _ => {} } diff --git a/hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs b/hac-client/src/pages/collection_viewer/request_editor/headers_editor_edit_form.rs similarity index 78% rename from hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs rename to hac-client/src/pages/collection_viewer/request_editor/headers_editor_edit_form.rs index 21acc17..d38e74c 100644 --- a/hac-client/src/pages/collection_viewer/request_editor/headers_editor_form.rs +++ b/hac-client/src/pages/collection_viewer/request_editor/headers_editor_edit_form.rs @@ -1,5 +1,3 @@ -use hac_core::collection::header_map::HeaderMap; - use crate::ascii::LOGO_ASCII; use crate::pages::collection_viewer::collection_store::CollectionStore; use crate::pages::collection_viewer::collection_viewer::CollectionViewerOverlay; @@ -11,7 +9,7 @@ use std::cell::RefCell; use std::ops::{Add, Div}; use std::rc::Rc; -use crossterm::event::{KeyCode, KeyEvent}; +use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use rand::Rng; use ratatui::layout::Rect; use ratatui::style::Stylize; @@ -22,6 +20,8 @@ use ratatui::Frame; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum HeadersEditorFormEvent { FinishEdit, + CancelEdit, + Quit, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -46,6 +46,8 @@ pub struct HeadersEditorForm<'hef> { header_idx: usize, logo_idx: usize, focused_input: HeadersEditorFormInput, + original_name: String, + original_value: String, } impl<'hef> HeadersEditorForm<'hef> { @@ -61,14 +63,46 @@ impl<'hef> HeadersEditorForm<'hef> { collection_store, logo_idx, focused_input: HeadersEditorFormInput::Name, + original_name: String::default(), + original_value: String::default(), } } pub fn update(&mut self, header_idx: usize) -> anyhow::Result<()> { self.header_idx = header_idx; + if !self.original_name.is_empty() || !self.original_value.is_empty() { + return Ok(()); + } + + let store = self.collection_store.borrow_mut(); + let Some(request) = store.get_selected_request() else { + anyhow::bail!("trying to edit a header without a selected request"); + }; + + let request = request.read().unwrap(); + let Some(ref headers) = request.headers else { + anyhow::bail!("trying to edit a header that don't exist"); + }; + + let CollectionViewerOverlay::HeadersForm(idx) = store.peek_overlay() else { + anyhow::bail!("tried to display the header form without the proper overlay set"); + }; + + let header = headers + .get(idx) + .expect("selected a non-existing header to edit"); + + self.original_name = header.pair.0.to_string(); + self.original_value = header.pair.1.to_string(); + Ok(()) } + + fn reset(&mut self) { + self.original_name.clear(); + self.original_value.clear(); + } } impl Renderable for HeadersEditorForm<'_> { @@ -187,6 +221,10 @@ impl Eventful for HeadersEditorForm<'_> { anyhow::bail!("selected header being edited doesnt exist on request"); }; + if let (KeyCode::Char('c'), KeyModifiers::CONTROL) = (key_event.code, key_event.modifiers) { + return Ok(Some(HeadersEditorFormEvent::Quit)); + } + match key_event.code { KeyCode::Tab => self.focused_input = self.focused_input.next(), KeyCode::BackTab => self.focused_input = self.focused_input.next(), @@ -198,10 +236,20 @@ impl Eventful for HeadersEditorForm<'_> { HeadersEditorFormInput::Name => header.pair.0.push(c), HeadersEditorFormInput::Value => header.pair.1.push(c), }, - KeyCode::Esc => {} + KeyCode::Esc => { + header.pair = (self.original_name.clone(), self.original_value.clone()); + drop(store); + self.reset(); + return Ok(Some(HeadersEditorFormEvent::CancelEdit)); + } + KeyCode::Enter => { + drop(store); + self.reset(); + return Ok(Some(HeadersEditorFormEvent::FinishEdit)); + } _ => {} }; - Ok(Some(HeadersEditorFormEvent::FinishEdit)) + Ok(None) } } diff --git a/hac-core/src/collection.rs b/hac-core/src/collection.rs index 2143a07..e06ccc4 100755 --- a/hac-core/src/collection.rs +++ b/hac-core/src/collection.rs @@ -1,6 +1,5 @@ #[allow(clippy::module_inception)] pub mod collection; -pub mod header_map; pub mod types; pub use types::Collection; mod errors; diff --git a/hac-core/src/collection/header_map.rs b/hac-core/src/collection/header_map.rs deleted file mode 100644 index 3ef532c..0000000 --- a/hac-core/src/collection/header_map.rs +++ /dev/null @@ -1,34 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Deserialize, Serialize, Clone)] -pub struct HeaderMap { - pub pair: (String, String), - pub enabled: bool, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum HeaderMapError { - InvalidName(String), - InvalidValue(String), -} - -impl HeaderMap { - pub fn from_pair((name, value): (String, String)) -> anyhow::Result { - if name.is_empty() { - return Err(HeaderMapError::InvalidName( - "header name cannot be empty".into(), - )); - }; - - if value.is_empty() { - return Err(HeaderMapError::InvalidValue( - "header value cannot be empty".into(), - )); - } - - Ok(HeaderMap { - pair: (name, value), - enabled: true, - }) - } -} diff --git a/hac-core/src/collection/types.rs b/hac-core/src/collection/types.rs index 255677f..819f10a 100755 --- a/hac-core/src/collection/types.rs +++ b/hac-core/src/collection/types.rs @@ -4,8 +4,6 @@ use std::sync::{Arc, RwLock}; use serde::{Deserialize, Serialize}; -use crate::collection::header_map::HeaderMap; - #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Collection { pub info: Info, @@ -43,6 +41,12 @@ impl RequestKind { } } +#[derive(Debug, Deserialize, Serialize, Clone)] +pub struct HeaderMap { + pub pair: (String, String), + pub enabled: bool, +} + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Clone)] #[serde(rename_all = "UPPERCASE")] pub enum RequestMethod { diff --git a/hac-core/src/net.rs b/hac-core/src/net.rs index 39a91a3..70ac50c 100755 --- a/hac-core/src/net.rs +++ b/hac-core/src/net.rs @@ -1,3 +1,4 @@ +pub mod request_client; pub mod request_manager; pub mod request_strategies; pub mod response_decoders; diff --git a/hac-core/src/net/request_client.rs b/hac-core/src/net/request_client.rs new file mode 100644 index 0000000..4401408 --- /dev/null +++ b/hac-core/src/net/request_client.rs @@ -0,0 +1,61 @@ +use crate::collection::types::Request; + +#[derive(Debug)] +pub struct RequestClient { + client: reqwest::Client, +} + +impl RequestClient { + pub fn new() -> Self { + RequestClient { + client: reqwest::Client::new(), + } + } + + pub fn get(&self, request: &Request) -> reqwest::RequestBuilder { + let request_builder = self.client.get(&request.uri); + self.append_headers(request, request_builder) + } + + pub fn post(&self, request: &Request) -> reqwest::RequestBuilder { + let request_builder = self.client.post(&request.uri); + self.append_headers(request, request_builder) + } + + pub fn put(&self, request: &Request) -> reqwest::RequestBuilder { + let request_builder = self.client.put(&request.uri); + self.append_headers(request, request_builder) + } + + pub fn patch(&self, request: &Request) -> reqwest::RequestBuilder { + let request_builder = self.client.patch(&request.uri); + self.append_headers(request, request_builder) + } + + pub fn delete(&self, request: &Request) -> reqwest::RequestBuilder { + let request_builder = self.client.delete(&request.uri); + self.append_headers(request, request_builder) + } + + fn append_headers( + &self, + request: &Request, + mut request_builder: reqwest::RequestBuilder, + ) -> reqwest::RequestBuilder { + if let Some(ref headers) = request.headers { + for header in headers.iter().filter(|header| header.enabled) { + let header_name = header.pair.0.clone(); + let header_value = header.pair.1.clone(); + request_builder = request_builder.header(header_name, header_value); + } + } + + request_builder + } +} + +impl Default for RequestClient { + fn default() -> Self { + Self::new() + } +} diff --git a/hac-core/src/net/request_strategies/http_strategy.rs b/hac-core/src/net/request_strategies/http_strategy.rs index 9bedc6f..7b9be5b 100755 --- a/hac-core/src/net/request_strategies/http_strategy.rs +++ b/hac-core/src/net/request_strategies/http_strategy.rs @@ -1,4 +1,5 @@ use crate::collection::types::{Request, RequestMethod}; +use crate::net::request_client::RequestClient; use crate::net::request_manager::Response; use crate::net::request_strategies::RequestStrategy; use crate::net::response_decoders::{decoder_from_headers, ResponseDecoder}; @@ -7,7 +8,7 @@ pub struct HttpResponse; impl RequestStrategy for HttpResponse { async fn handle(&self, request: Request) -> Response { - let client = reqwest::Client::new(); + let client = RequestClient::default(); match request.method { RequestMethod::Get => self.handle_get_request(client, request).await, @@ -20,9 +21,9 @@ impl RequestStrategy for HttpResponse { } impl HttpResponse { - async fn handle_get_request(&self, client: reqwest::Client, request: Request) -> Response { + async fn handle_get_request(&self, client: RequestClient, request: Request) -> Response { let now = std::time::Instant::now(); - match client.get(request.uri).send().await { + match client.get(&request).send().await { Ok(response) => { let decoder = decoder_from_headers(response.headers()); decoder.decode(response, now).await @@ -42,10 +43,10 @@ impl HttpResponse { } } - async fn handle_post_request(&self, client: reqwest::Client, request: Request) -> Response { + async fn handle_post_request(&self, client: RequestClient, request: Request) -> Response { let now = std::time::Instant::now(); match client - .post(request.uri) + .post(&request) .json(&request.body.unwrap_or_default()) .send() .await @@ -69,10 +70,10 @@ impl HttpResponse { } } - async fn handle_put_request(&self, client: reqwest::Client, request: Request) -> Response { + async fn handle_put_request(&self, client: RequestClient, request: Request) -> Response { let now = std::time::Instant::now(); match client - .put(request.uri) + .put(&request) .json(&request.body.unwrap_or_default()) .send() .await @@ -96,10 +97,10 @@ impl HttpResponse { } } - async fn handle_patch_request(&self, client: reqwest::Client, request: Request) -> Response { + async fn handle_patch_request(&self, client: RequestClient, request: Request) -> Response { let now = std::time::Instant::now(); match client - .patch(request.uri) + .patch(&request) .json(&request.body.unwrap_or_default()) .send() .await @@ -123,10 +124,10 @@ impl HttpResponse { } } - async fn handle_delete_request(&self, client: reqwest::Client, request: Request) -> Response { + async fn handle_delete_request(&self, client: RequestClient, request: Request) -> Response { let now = std::time::Instant::now(); match client - .delete(request.uri) + .delete(&request) .json(&request.body.unwrap_or_default()) .send() .await