Skip to content
This repository has been archived by the owner on Oct 8, 2024. It is now read-only.

Commit

Permalink
docs: readme updates
Browse files Browse the repository at this point in the history
  • Loading branch information
PThorpe92 committed Apr 5, 2024
1 parent 4c84d00 commit 6021402
Show file tree
Hide file tree
Showing 12 changed files with 68 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "CuTE-tui" # crates.io/crates/CuTE is taken :(
version = "0.1.0"
name = "CuTE-tui" # crates.io/crates/CuTE is taken :(
version = "0.1.1"
authors = ["PThorpe92 <[email protected]>"]
description = "A (ratatui) TUI for HTTP requests with libcurl, recursive downloads with wget, command storage and API key management"
description = "A (ratatui) TUI for HTTP requests with request + API key management"
license = "GPL-3.0"
edition = "2021"
repository = "https://github.com/PThorpe92/CuTE"
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,33 @@

# TUI HTTP Client with API/Auth Key Management and Request History/Storage

#### This project is still in active development and although it is useable, there may still be bugs and significant changes are still needed to both refactor the codebase and add new features.
#### Collaboration is welcome and encouraged! There is lots of low hanging fruit 👍 and cool ideas for additional features.
> This project is still in active development and although it is useable, there may still be bugs and significant changes are still needed to both refactor the codebase and add new features.
### Collaboration is welcome and encouraged! There is lots of low hanging fruit 👍 and cool ideas for additional features.
![image](imgs/demo.gif)

Curl TUI Environment (CuTE). HTTP client/libcurl front-end in Rust, using the awesome [ratatui](https://github.com/ratatui-org/ratatui) library designed to simplify the process of making various types of HTTP requests, supporting various kinds of Authentication (powered by libcURL), recursive downloading of directories (powered by GNU Wget), and storage + management of your previous requests + API keys.
Curl TUI Environment (CuTE). HTTP client/libcurl front-end in Rust, using the awesome [ratatui](https://github.com/ratatui-org/ratatui) library designed to simplify the process of sending HTTP requests in the terminal, allowing you to store and manage your previous requests + API keys.

This tool is for when you don't need something as complex as Postman, but you also don't want to have to remember the syntax for `curl` (or `wget`) commands. You can make a few requests to your back-end for testing, set up with your API key and save the requests to be executed again later.
This is a tool for when your requests are not complex enough for something like Postman, but more complicated than you would want to use `curl` CLI, or if you just don't want to remember all those commands. You can make a few requests to your back-ends for testing, set up with your API key and save the requests to be executed again later.

**New**: We now support importing your `Postman` collections, so you can have easy access to your APIs without leaving the terminal. (note: `https://schema.getpostman.com/json/collection/v2.1.0/` is currently the only supported collection schema)

<img src="imgs/saved_request.png" class="img-rounded " align="center" />
<img src="imgs/api_keys.png" class="img-rounded " align="center" />

## Features

- **Interactive TUI Interface**: The application offers an intuitive TUI interface that makes it easy to construct and execute HTTP requests without leaving the terminal.
- **Interactive TUI Interface**: Intuitive TUI interface that makes it fast and easy to construct and execute HTTP requests without leaving the terminal.

- **Intuitive VIM keybindings:** Vim-like keybindings are _defaulted_. Support to change them will eventually make it into the config file.
(`h` or `b` is used to go back a page, `j` and `k` move the cursor up and down. `i` for insert mode while in an input box, `enter` to submit the form and `esc` to exit insert mode)

- **API Key Management**: Very simple sqlite based API key storage system. You can choose to save a Key from a request, or add/edit/delete/rename them.

- **Response Visualization**: Pretty-print JSON responses in a human-readable format within the TUI, or choose to write the response to a file after inspecting the response.
- **Postman Collections**: Import your postman collections to have easy access to your APIs without leaving the terminal.

- **Response Visualization**: Pretty-print JSON responses in a human-readable format within the TUI, then you can choose to write the response to a file after inspecting it. You an also copy the `curl` CLI command needed to make the same request to your clipboard.

- This application builds and runs on Linux **and** MacOS.
- This application builds and runs on Linux, Windows and MacOS.

## Why?

Expand Down
Binary file added imgs/api_keys.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified imgs/saved_request.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ impl<'a> App<'a> {
.map(|x| x.get_name().to_string())
.collect::<Vec<String>>(),
),
Screen::SavedCollections => Some(
Screen::SavedCollections(_) => Some(
self.get_collections()
.unwrap_or_default()
.into_iter()
Expand Down Expand Up @@ -420,7 +420,7 @@ impl<'a> App<'a> {

pub fn rename_collection(&mut self, id: i32, name: &str) -> Result<(), rusqlite::Error> {
self.db.rename_collection(id, name)?;
self.goto_screen(&Screen::SavedCollections);
self.goto_screen(&Screen::SavedCollections(None));
Ok(())
}

Expand Down
5 changes: 5 additions & 0 deletions src/events/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ pub fn handle_key_events(key_event: KeyEvent, app: &mut App) -> AppResult<()> {
app.input_mode = InputMode::Editing;
}
}
Screen::SavedCollections(opt) if opt.is_some() => {
if !opt.as_ref().unwrap().is_error() {
app.input_mode = InputMode::Editing;
}
}
Screen::RequestBodyInput => {
app.input_mode = InputMode::Editing;
}
Expand Down
23 changes: 17 additions & 6 deletions src/screens/collections.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
use super::Screen;
use super::{error_alert_box, Screen};
use crate::app::App;
use crate::display::inputopt::InputOpt;
use crate::display::menuopts::{
COLLECTION_ALERT_MENU_OPTS, DEFAULT_MENU_PARAGRAPH, POSTMAN_COLLECTION_TITLE,
};
use crate::screens::render::handle_screen_defaults;
use crate::screens::ScreenArea;
use crate::screens::{centered_rect, render::render_header_paragraph};
use crate::screens::{
centered_rect, input::input::handle_default_input_screen, render::render_header_paragraph,
ScreenArea,
};
use tui::prelude::{Constraint, Direction, Layout, Margin};
use tui::style::{Color, Modifier, Style};
use tui::widgets::{Block, Borders, List, ListItem, ListState, Paragraph};
use tui::Frame;

pub fn handle_collection_menu(app: &mut App, frame: &mut Frame<'_>) {
pub fn handle_collection_menu(app: &mut App, frame: &mut Frame<'_>, opt: Option<InputOpt>) {
handle_screen_defaults(app, frame);
match opt {
Some(InputOpt::RequestError(e)) => {
error_alert_box(frame, &e);
}
Some(opt) => {
handle_default_input_screen(app, frame, opt.clone());
}
_ => {}
};
match app.selected {
// Import New Collection
Some(0) => app.goto_screen(&Screen::InputMenu(InputOpt::ImportCollection)),
Some(0) => app.goto_screen(&Screen::SavedCollections(Some(InputOpt::ImportCollection))),
// Create New Collection
Some(1) => app.goto_screen(&Screen::InputMenu(InputOpt::CreateCollection)),
Some(1) => app.goto_screen(&Screen::SavedCollections(Some(InputOpt::CreateCollection))),
// View Saved Collections
Some(2) => app.goto_screen(&Screen::ViewSavedCollections),
// Cancel
Expand Down
17 changes: 11 additions & 6 deletions src/screens/input/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub fn get_input_prompt(opt: InputOpt) -> Text<'static> {
AuthType::Bearer => Text::from(INPUT_OPT_AUTH_BEARER),
_ => Text::from(INPUT_OPT_AUTH_ANY),
},
InputOpt::CreateCollection => Text::from("Enter a name for the new collection"),
InputOpt::CookiePath => Text::from("Enter the path to the cookie jar file"),
InputOpt::NewCookie => Text::from("Enter the name of the cookie"),
InputOpt::CookieValue(ref name) => Text::from(format!("Enter the value for {}", name)),
Expand All @@ -52,7 +53,7 @@ pub fn handle_default_input_screen(app: &mut App, frame: &mut Frame<'_>, opt: In
let text_chunk = Block::default().borders(Borders::ALL).style(
Style::default()
.bg(app.config.get_bg_color())
.fg(Color::LightGreen)
.fg(Color::LightBlue)
.add_modifier(tui::style::Modifier::BOLD),
);
frame.render_widget(text_chunk, input_textbox);
Expand All @@ -61,7 +62,7 @@ pub fn handle_default_input_screen(app: &mut App, frame: &mut Frame<'_>, opt: In
frame.render_widget(
Paragraph::new(prompt).style(
Style::default()
.fg(Color::LightGreen)
.fg(Color::LightBlue)
.add_modifier(Modifier::BOLD),
),
centered_rect(bottom_box, crate::screens::ScreenArea::Top),
Expand Down Expand Up @@ -152,7 +153,7 @@ pub fn handle_default_input_screen(app: &mut App, frame: &mut Frame<'_>, opt: In
InputMode::Normal => (
Line::from("Press 'i' to start editing"),
Style::default()
.fg(Color::Green)
.fg(Color::LightBlue)
.add_modifier(tui::style::Modifier::BOLD),
),
InputMode::Editing => (
Expand Down Expand Up @@ -228,7 +229,7 @@ pub fn parse_input(message: String, opt: InputOpt, app: &mut App) {
}
InputOpt::RenameCollection(ref id) => {
if app.rename_collection(*id, &message).is_ok() {
app.goto_screen(&Screen::SavedCollections);
app.goto_screen(&Screen::SavedCollections(None));
} else {
app.goto_screen(&Screen::Error("Failed to rename collection".to_string()));
}
Expand Down Expand Up @@ -316,10 +317,14 @@ pub fn parse_input(message: String, opt: InputOpt, app: &mut App) {
}
InputOpt::CreateCollection => {
if app.create_postman_collection(&message).is_ok() {
app.goto_screen(&Screen::Success);
app.goto_screen(&Screen::SavedCollections(Some(InputOpt::RequestError(
"Successfully created collection".to_string(),
))));
return;
}
app.goto_screen(&Screen::Error("Failed to create collection".to_string()));
app.goto_screen(&Screen::SavedCollections(Some(InputOpt::RequestError(
"Failed to create collection".to_string(),
))));
}
InputOpt::KeyLabel(id) => match app.set_key_label(id, &message) {
Ok(_) => app.goto_screen(&Screen::SavedKeys),
Expand Down
14 changes: 10 additions & 4 deletions src/screens/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,16 @@ pub fn error_alert_box(frame: &mut Frame<'_>, error_message: &str) -> Rect {

// Render the alert box
let alert_box = layout[0];
let alert_text_chunk = Block::default()
.borders(Borders::ALL)
.style(Style::default().bg(Color::Red).fg(Color::White))
.title("Alert");
let alert_text_chunk = match error_message.starts_with("Error:") {
true => Block::default()
.borders(Borders::ALL)
.style(Style::default().bg(Color::Red).fg(Color::White))
.title("Alert"),
false => Block::default()
.borders(Borders::ALL)
.style(Style::default().bg(Color::LightBlue).fg(Color::White))
.title("Success"),
};
frame.render_widget(alert_text_chunk, alert_box);
let text = Text::styled(
error_message,
Expand Down
8 changes: 4 additions & 4 deletions src/screens/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ pub fn handle_screen_defaults(app: &mut App, frame: &mut Frame<'_>) {
Screen::ViewBody => (&DEFAULT_MENU_PARAGRAPH, &VIEW_BODY_TITLE),
Screen::SavedKeys => (&API_KEY_PARAGRAPH, &API_KEY_TITLE),
Screen::HeaderAddRemove => (&DEFAULT_MENU_PARAGRAPH, &DEFAULT_MENU_TITLE),
Screen::SavedCollections => (&DEFAULT_MENU_PARAGRAPH, &POSTMAN_COLLECTION_TITLE),
Screen::SavedCollections(_) => (&DEFAULT_MENU_PARAGRAPH, &POSTMAN_COLLECTION_TITLE),
_ => (&DEFAULT_MENU_PARAGRAPH, &DEFAULT_MENU_TITLE),
};
frame.render_widget(
Expand All @@ -119,7 +119,7 @@ pub fn handle_screen(app: &mut App, frame: &mut Frame<'_>, screen: Screen) {
match num {
0 => app.goto_screen(&Screen::Method),
1 => app.goto_screen(&Screen::SavedCommands(None)),
2 => app.goto_screen(&Screen::SavedCollections),
2 => app.goto_screen(&Screen::SavedCollections(None)),
3 => app.goto_screen(&Screen::SavedKeys),
_ => {}
}
Expand Down Expand Up @@ -184,8 +184,8 @@ pub fn handle_screen(app: &mut App, frame: &mut Frame<'_>, screen: Screen) {
InputOpt::RequestBody,
),
Screen::KeysMenu(cmd) => handle_key_menu(app, frame, cmd),
Screen::SavedCollections => {
super::collections::handle_collection_menu(app, frame);
Screen::SavedCollections(opt) => {
super::collections::handle_collection_menu(app, frame, opt);
}
Screen::ViewSavedCollections => {
super::collections::handle_collections_screen(app, frame);
Expand Down
6 changes: 3 additions & 3 deletions src/screens/screen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub enum Screen {
RequestMenu(Option<InputOpt>),
InputMenu(InputOpt),
Response(String),
SavedCollections,
SavedCollections(Option<InputOpt>),
ViewSavedCollections,
Authentication,
Success,
Expand Down Expand Up @@ -56,7 +56,7 @@ impl Display for Screen {
Screen::CmdMenu(_) => "CmdMenu",
Screen::KeysMenu(_) => "KeysMenu",
Screen::RequestBodyInput => "RequestBodyInput",
Screen::SavedCollections => "Saved Collections",
Screen::SavedCollections(_) => "Saved Collections",
Screen::ViewSavedCollections => "View Saved Collections",
Screen::ColMenu(_) => "Collection Menu",
Screen::CookieOptions => "Cookie Options",
Expand Down Expand Up @@ -187,7 +187,7 @@ impl<'a> Screen {
.map(|c| ListItem::new(format!("{}{}", c, OPTION_PADDING_MIN)))
.collect(),

Screen::SavedCollections => COLLECTION_MENU_OPTIONS
Screen::SavedCollections(_) => COLLECTION_MENU_OPTIONS
.iter()
.map(|i| ListItem::new(format!("{}{}", i, OPTION_PADDING_MAX)))
.collect(),
Expand Down

0 comments on commit 6021402

Please sign in to comment.