Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions crates/crabapi/src/core/requests/constants.rs
Original file line number Diff line number Diff line change
@@ -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";
Original file line number Diff line number Diff line change
@@ -1,40 +1,13 @@
pub mod constants;
pub mod validators;

use reqwest::{Body, Client, Error, RequestBuilder, Response};
use std::collections::HashMap;
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,
Expand Down
3 changes: 3 additions & 0 deletions crates/crabapi/src/core/requests/validators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub fn is_valid_url(url: &str) -> bool {
super::Url::parse(url).is_ok()
}
15 changes: 15 additions & 0 deletions crates/crabapi/src/gui/iced/default_styles.rs
Original file line number Diff line number Diff line change
@@ -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)
}
182 changes: 118 additions & 64 deletions crates/crabapi/src/gui/iced/mod.rs
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -13,8 +18,6 @@ pub fn init() {
enum Message {
MethodChanged(Method),
UrlInputChanged(String),
// HeaderInputChanged(String),
// BodyInputChanged(String),
SendRequest,
HeaderKeyChanged(usize, String),
HeaderValueChanged(usize, String),
Expand All @@ -25,7 +28,7 @@ enum Message {

#[derive(Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct GUI {
struct GUI {
methods: combo_box::State<Method>,
method_selected: Option<Method>,
url_input: String,
Expand All @@ -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(),
}
}

Expand All @@ -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
}
Expand All @@ -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<Message> {
// 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<Message> {
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<Message> {
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::Font> {
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<Message> {
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<Message> {
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<Message> {
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<Message> {
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()
}
}

Expand Down