diff --git a/crates/crabapi/src/core/requests/constants.rs b/crates/crabapi/src/core/requests/constants.rs new file mode 100644 index 0000000..e7bc74e --- /dev/null +++ b/crates/crabapi/src/core/requests/constants.rs @@ -0,0 +1,27 @@ +use const_format::formatcp; +use http::Method; + +pub const USER_AGENT: &str = formatcp!( + "{} v{}", + crate::core::app::constants::APP_NAME, + crate::core::app::constants::APP_VERSION +); + +pub const METHODS: [Method; 9] = [ + Method::GET, + Method::POST, + Method::PUT, + Method::DELETE, + Method::HEAD, + Method::OPTIONS, + Method::CONNECT, + Method::PATCH, + Method::TRACE, +]; + +pub const METHODS_STRING: [&str; 9] = [ + "GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "CONNECT", "PATCH", "TRACE", +]; + +pub const ALL_METHODS_AS_STRING: &str = + "GET, POST, PUT, DELETE, HEAD, OPTIONS, CONNECT, PATCH, TRACE"; diff --git a/crates/crabapi/src/core/requests.rs b/crates/crabapi/src/core/requests/mod.rs similarity index 68% rename from crates/crabapi/src/core/requests.rs rename to crates/crabapi/src/core/requests/mod.rs index a90aed7..bec12d5 100644 --- a/crates/crabapi/src/core/requests.rs +++ b/crates/crabapi/src/core/requests/mod.rs @@ -1,3 +1,6 @@ +pub mod constants; +pub mod validators; + use reqwest::{Body, Client, Error, RequestBuilder, Response}; use std::collections::HashMap; use tokio::task::JoinHandle; @@ -5,36 +8,6 @@ use tokio::task::JoinHandle; pub use http::{HeaderMap, Method}; pub use reqwest::Url; -pub mod constants { - use const_format::formatcp; - use http::Method; - - pub const USER_AGENT: &str = formatcp!( - "{} v{}", - crate::core::app::constants::APP_NAME, - crate::core::app::constants::APP_VERSION - ); - - pub const METHODS: [Method; 9] = [ - Method::GET, - Method::POST, - Method::PUT, - Method::DELETE, - Method::HEAD, - Method::OPTIONS, - Method::CONNECT, - Method::PATCH, - Method::TRACE, - ]; - - pub const METHODS_STRING: [&str; 9] = [ - "GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "CONNECT", "PATCH", "TRACE", - ]; - - pub const ALL_METHODS_AS_STRING: &str = - "GET, POST, PUT, DELETE, HEAD, OPTIONS, CONNECT, PATCH, TRACE"; -} - // TODO: Implement params too pub fn build_request( client: &Client, diff --git a/crates/crabapi/src/core/requests/validators.rs b/crates/crabapi/src/core/requests/validators.rs new file mode 100644 index 0000000..837526d --- /dev/null +++ b/crates/crabapi/src/core/requests/validators.rs @@ -0,0 +1,3 @@ +pub fn is_valid_url(url: &str) -> bool { + super::Url::parse(url).is_ok() +} diff --git a/crates/crabapi/src/gui/iced/default_styles.rs b/crates/crabapi/src/gui/iced/default_styles.rs new file mode 100644 index 0000000..c994c4f --- /dev/null +++ b/crates/crabapi/src/gui/iced/default_styles.rs @@ -0,0 +1,15 @@ +pub const fn input_size_as_f32() -> f32 { + 20.0 +} + +pub const fn input_size() -> iced::Pixels { + iced::Pixels(input_size_as_f32()) +} + +pub const fn padding() -> iced::Padding { + iced::Padding::new(10.0) +} + +pub const fn spacing() -> iced::Pixels { + iced::Pixels(10.0) +} diff --git a/crates/crabapi/src/gui/iced/mod.rs b/crates/crabapi/src/gui/iced/mod.rs index 8d9d386..d7de579 100644 --- a/crates/crabapi/src/gui/iced/mod.rs +++ b/crates/crabapi/src/gui/iced/mod.rs @@ -1,9 +1,14 @@ +// internal mods +mod default_styles; + +// dependencies use iced; -use iced::widget::{Button, Text, TextInput, column, row}; -use iced::widget::{button, combo_box, container}; -use iced::{Element, Length}; +use iced::widget::{Button, Row, Text, TextInput}; +use iced::widget::{button, column, combo_box, container, row}; +use iced::{Alignment, Element, Length}; -use crate::core::requests::{Method, Url, constants}; +// internal dependencies +use crate::core::requests::{Method, constants, validators}; pub fn init() { iced::run(GUI::title, GUI::update, GUI::view).unwrap() @@ -13,8 +18,6 @@ pub fn init() { enum Message { MethodChanged(Method), UrlInputChanged(String), - // HeaderInputChanged(String), - // BodyInputChanged(String), SendRequest, HeaderKeyChanged(usize, String), HeaderValueChanged(usize, String), @@ -25,7 +28,7 @@ enum Message { #[derive(Debug)] #[allow(clippy::upper_case_acronyms)] -pub struct GUI { +struct GUI { methods: combo_box::State, method_selected: Option, url_input: String, @@ -37,11 +40,10 @@ impl GUI { fn new() -> Self { Self { methods: combo_box::State::new(constants::METHODS.into()), - method_selected: None, + method_selected: Some(Method::GET), url_input: String::new(), url_input_valid: false, header_input: vec![(String::new(), String::new())], - // body_input: String::new(), } } @@ -56,13 +58,8 @@ impl GUI { } Message::UrlInputChanged(url) => { self.url_input = url; - self.url_input_valid = GUI::is_valid_url(&self.url_input); - } // Message::HeaderInputChanged(header) => { - // self.header_input = header; // ttp::HeaderMap::new(); - // } - // Message::BodyInputChanged(body) => { - // self.body_input = body; // http::Body::from("Body"); - // } + self.url_input_valid = validators::is_valid_url(&self.url_input); + } Message::SendRequest => { // TODO } @@ -79,82 +76,139 @@ impl GUI { Message::AddHeader => { self.header_input.push((String::new(), String::new())); } - _ => {} + _ => {} // TODO: REmove this. Unnecessary if all implemented and enum is non-exhaustive } } fn view(&self) -> Element { // ROW: Method, URI, Send Button - let url_input_icon = iced::widget::text_input::Icon { - font: iced::Font::default(), - code_point: if self.url_input_valid { '✅' } else { '❌' }, - size: Some(Self::input_size()), - spacing: 0.0, - side: iced::widget::text_input::Side::Right, - }; + let request_row = self.view_request(); + + // ROW: Headers + let headers_column = self.view_request_headers(); + + column![ + request_row, + container(headers_column) + .width(Length::Fill) + .padding(default_styles::padding()) + ] + .into() + } + fn view_request(&self) -> Element { + let url_input = self.view_request_url_input(); + + let method_combo_box = self.view_request_method_input(); + + let send_button = Self::view_request_send_button(); + + let request_row = + Self::view_request_row_setup(row![method_combo_box, url_input, send_button]); + + request_row.into() + } + + // VIEW REQUEST - GENERAL + + fn view_request_url_input(&self) -> Element { + let url_input_icon = Self::view_request_url_input_icon(self.url_input_valid); let url_input = TextInput::new("Enter URI", &self.url_input) .on_input(Message::UrlInputChanged) - .size(Self::input_size()) + .size(default_styles::input_size()) .icon(url_input_icon) .width(Length::Fill); - let method_combo_box = combo_box( + + url_input.into() + } + + fn view_request_url_input_icon(valid: bool) -> iced::widget::text_input::Icon { + iced::widget::text_input::Icon { + font: iced::Font::default(), + code_point: if valid { '✅' } else { '❌' }, + size: Some(default_styles::input_size()), + spacing: 0.0, + side: iced::widget::text_input::Side::Right, + } + } + + fn view_request_method_input(&self) -> Element { + combo_box( &self.methods, "Method", self.method_selected.as_ref(), Message::MethodChanged, ) .width(75) - .size(Self::input_size_as_f32()); + .size(default_styles::input_size_as_f32()) + .into() + } - let send_button = Button::new(Text::new("Send").size(20)).on_press(Message::SendRequest); - let request_row = row![method_combo_box, url_input, send_button] - .spacing(10) - .padding(10) - .align_y(iced::Alignment::Center); + fn view_request_row_setup(request_row: Row<'_, Message>) -> Row<'_, Message> { + request_row + .spacing(default_styles::spacing()) + .padding(default_styles::padding()) + .align_y(Alignment::Center) + } - // ROW: Headers - let headers_title = Text::new("Headers").size(16); - let mut headers_column = column![headers_title].spacing(10); + fn view_request_send_button() -> Element<'static, Message> { + Button::new(Text::new("Send").size(default_styles::input_size())) + .on_press(Message::SendRequest) + .into() + } - for (i, header) in self.header_input.iter().enumerate() { - let header_row = row![ - TextInput::new("Key", &header.0) - .on_input(move |key| Message::HeaderKeyChanged(i, key)) - .width(Length::FillPortion(1)), - TextInput::new("Value", &header.1) // TODO: Change unwrap - .on_input(move |value| Message::HeaderValueChanged(i, value)) - .width(Length::FillPortion(2)), - Button::new(Text::new("X")) - .on_press(Message::RemoveHeader(i)) - .style(button::danger) - ] - .spacing(10); + // VIEW REQUEST - HEADERS - headers_column = headers_column.push(header_row); - } + fn view_request_headers(&self) -> Element { + let headers_title = Self::view_request_headers_title(); - let add_header_button = - Button::new(Text::new("Add Header").size(20)).on_press(Message::AddHeader); - headers_column = headers_column.push(add_header_button); + let headers_column = self.view_request_headers_column(); - column![ - request_row, - container(headers_column).width(Length::Fill).padding(10) - ] - .into() + let header_add_button = Self::view_request_headers_add_button(); + + column![headers_title, headers_column, header_add_button] + .spacing(default_styles::spacing()) + .into() + } + + fn view_request_headers_title() -> Element<'static, Message> { + Text::new("Headers").size(16).into() } - fn is_valid_url(url: &str) -> bool { - Url::parse(url).is_ok() + fn view_request_headers_column(&self) -> Element { + let mut headers_column = column![]; + + for (i, header) in self.header_input.iter().enumerate() { + let header_row = self.view_request_headers_column_row(i, header); + headers_column = headers_column.push(header_row); + } + headers_column.spacing(default_styles::spacing()).into() } - const fn input_size_as_f32() -> f32 { - 20.0 + fn view_request_headers_column_row( + &self, + index: usize, + header: &(String, String), + ) -> Element { + row![ + TextInput::new("Key", &header.0) + .on_input(move |key| Message::HeaderKeyChanged(index, key)) + .width(Length::FillPortion(1)), + TextInput::new("Value", &header.1) // TODO: Change unwrap + .on_input(move |value| Message::HeaderValueChanged(index, value)) + .width(Length::FillPortion(2)), + Button::new(Text::new("X")) + .on_press(Message::RemoveHeader(index)) + .style(button::danger) + ] + .spacing(default_styles::spacing()) + .into() } - const fn input_size() -> iced::Pixels { - iced::Pixels(Self::input_size_as_f32()) + fn view_request_headers_add_button() -> Element<'static, Message> { + Button::new(Text::new("Add Header").size(default_styles::input_size())) + .on_press(Message::AddHeader) + .into() } }