Skip to content

Commit

Permalink
feat: ⚡ added tags and tag filtering
Browse files Browse the repository at this point in the history
changed focused pane border style
  • Loading branch information
zaghaghi committed Mar 6, 2024
1 parent 8bf24d5 commit d97476c
Show file tree
Hide file tree
Showing 9 changed files with 194 additions and 29 deletions.
25 changes: 18 additions & 7 deletions src/pages/home.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,29 @@ pub struct State {
pub openapi_path: String,
pub openapi_spec: Spec,
pub active_operation_index: usize,
pub active_tag_name: Option<String>,
}

impl State {
pub fn active_operation(&self) -> Option<(String, String, &Operation)> {
if let Some((path, method, operation)) = self.openapi_spec.operations().nth(self.active_operation_index) {
Some((path, method.to_string(), operation))
} else {
None
if let Some(active_tag) = &self.active_tag_name {
if let Some((path, method, operation)) =
self.openapi_spec.operations().filter(|item| item.2.tags.contains(active_tag)).nth(self.active_operation_index)
{
return Some((path, method.to_string(), operation));
}
} else if let Some((path, method, operation)) = self.openapi_spec.operations().nth(self.active_operation_index) {
return Some((path, method.to_string(), operation));
}
None
}

pub fn operations_len(&self) -> usize {
self.openapi_spec.operations().count()
if let Some(active_tag) = &self.active_tag_name {
self.openapi_spec.operations().filter(|item| item.2.tags.contains(active_tag)).count()
} else {
self.openapi_spec.operations().count()
}
}
}

Expand All @@ -51,7 +61,8 @@ pub struct Home {
impl Home {
pub fn new(openapi_path: String) -> Result<Self> {
let openapi_spec = oas3::from_path(openapi_path.clone())?;
let state = Arc::new(RwLock::new(State { openapi_spec, openapi_path, active_operation_index: 0 }));
let state =
Arc::new(RwLock::new(State { openapi_spec, openapi_path, active_operation_index: 0, active_tag_name: None }));
let focused_border_style = Style::default().fg(Color::LightGreen);

Ok(Self {
Expand All @@ -60,7 +71,7 @@ impl Home {
panes: vec![
Box::new(ProfilesPane::new(false, focused_border_style)),
Box::new(ApisPane::new(state.clone(), true, focused_border_style)),
Box::new(TagsPane::new(false, focused_border_style)),
Box::new(TagsPane::new(state.clone(), false, focused_border_style)),
Box::new(AddressPane::new(state.clone(), false, focused_border_style)),
Box::new(RequestPane::new(false, focused_border_style)),
Box::new(ResponsePane::new(false, focused_border_style)),
Expand Down
19 changes: 17 additions & 2 deletions src/panes/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ impl AddressPane {
}
}

fn border_type(&self) -> BorderType {
match self.focused {
true => BorderType::Thick,
false => BorderType::Plain,
}
}

fn method_color(method: &str) -> Color {
match method {
"GET" => Color::LightCyan,
Expand Down Expand Up @@ -86,7 +93,6 @@ impl Pane for AddressPane {
};
let title = operation.summary.clone().unwrap_or_default();
let inner_margin = Margin { horizontal: 2, vertical: 2 };
frame.render_widget(Block::default().title(title).borders(Borders::ALL).border_style(self.border_style()), area);
let inner = area.inner(&inner_margin);
frame.render_widget(
Paragraph::new(Line::from(vec![
Expand All @@ -95,7 +101,16 @@ impl Pane for AddressPane {
Span::styled(path, Style::default().fg(Color::White)),
])),
inner,
)
);

frame.render_widget(
Block::default()
.title(title)
.borders(Borders::ALL)
.border_style(self.border_style())
.border_type(self.border_type()),
area,
);
}

Ok(())
Expand Down
40 changes: 31 additions & 9 deletions src/panes/apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ pub struct ApisPane {

impl ApisPane {
pub fn new(state: Arc<RwLock<State>>, focused: bool, focused_border_style: Style) -> Self {
Self { focused, focused_border_style, state: state.clone(), current_operation_index: 0 }
Self { focused, focused_border_style, state, current_operation_index: 0 }
}

fn border_style(&self) -> Style {
Expand All @@ -33,6 +33,13 @@ impl ApisPane {
}
}

fn border_type(&self) -> BorderType {
match self.focused {
true => BorderType::Thick,
false => BorderType::Plain,
}
}

fn method_color(method: &str) -> Color {
match method {
"GET" => Color::LightCyan,
Expand Down Expand Up @@ -89,6 +96,10 @@ impl Pane for ApisPane {
state.active_operation_index = self.current_operation_index;
return Ok(Some(Action::Update));
},
Action::Update => {
let state = self.state.read().unwrap();
self.current_operation_index = state.active_operation_index;
},
_ => {},
}

Expand All @@ -98,8 +109,13 @@ impl Pane for ApisPane {
fn draw(&mut self, frame: &mut Frame<'_>, area: Rect) -> Result<()> {
let state = self.state.read().unwrap();
let unknown = String::from("Unknown");
let items = state.openapi_spec.operations().map(|operation| {
Line::from(vec![
let items = state.openapi_spec.operations().filter_map(|operation| {
if let Some(active_tag) = &state.active_tag_name {
if !operation.2.tags.contains(active_tag) {
return None;
}
}
Some(Line::from(vec![
Span::styled(
format!("{:7}", operation.1.as_str()),
Style::default().fg(Self::method_color(operation.1.as_str())),
Expand All @@ -108,7 +124,7 @@ impl Pane for ApisPane {
operation.2.summary.as_ref().unwrap_or(operation.2.operation_id.as_ref().unwrap_or(&unknown)),
Style::default().fg(Color::White),
),
])
]))
});

let list = List::new(items)
Expand All @@ -118,12 +134,18 @@ impl Pane for ApisPane {
let mut list_state = ListState::default().with_selected(Some(self.current_operation_index));

frame.render_stateful_widget(list, area, &mut list_state);

let active_tag = format!("[{}]", state.active_tag_name.clone().unwrap_or(String::from("ALL")));
frame.render_widget(
Block::default().title("APIs").borders(Borders::ALL).border_style(self.border_style()).title_bottom(
Line::from(format!("{} of {}", self.current_operation_index.saturating_add(1), state.operations_len()))
.right_aligned(),
),
Block::default()
.title("APIs")
.borders(Borders::ALL)
.border_style(self.border_style())
.border_type(self.border_type())
.title_bottom(
Line::from(format!("{} of {}", self.current_operation_index.saturating_add(1), state.operations_len()))
.right_aligned(),
)
.title(Line::styled(active_tag, Style::default().add_modifier(Modifier::ITALIC)).right_aligned()),
area,
);
Ok(())
Expand Down
17 changes: 15 additions & 2 deletions src/panes/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ impl ProfilesPane {
false => Style::default(),
}
}

fn border_type(&self) -> BorderType {
match self.focused {
true => BorderType::Thick,
false => BorderType::Plain,
}
}
}
impl Pane for ProfilesPane {
fn init(&mut self) -> Result<()> {
Expand Down Expand Up @@ -58,8 +65,14 @@ impl Pane for ProfilesPane {
}

fn draw(&mut self, frame: &mut Frame<'_>, area: Rect) -> Result<()> {
frame
.render_widget(Block::default().title("Profiles").borders(Borders::ALL).border_style(self.border_style()), area);
frame.render_widget(
Block::default()
.title("Profiles")
.borders(Borders::ALL)
.border_style(self.border_style())
.border_type(self.border_type()),
area,
);
Ok(())
}
}
17 changes: 15 additions & 2 deletions src/panes/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ impl RequestPane {
false => Style::default(),
}
}

fn border_type(&self) -> BorderType {
match self.focused {
true => BorderType::Thick,
false => BorderType::Plain,
}
}
}
impl Pane for RequestPane {
fn init(&mut self) -> Result<()> {
Expand Down Expand Up @@ -58,8 +65,14 @@ impl Pane for RequestPane {
}

fn draw(&mut self, frame: &mut Frame<'_>, area: Rect) -> Result<()> {
frame
.render_widget(Block::default().title("Request").borders(Borders::ALL).border_style(self.border_style()), area);
frame.render_widget(
Block::default()
.title("Request")
.borders(Borders::ALL)
.border_style(self.border_style())
.border_type(self.border_type()),
area,
);
Ok(())
}
}
17 changes: 15 additions & 2 deletions src/panes/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ impl ResponsePane {
false => Style::default(),
}
}

fn border_type(&self) -> BorderType {
match self.focused {
true => BorderType::Thick,
false => BorderType::Plain,
}
}
}
impl Pane for ResponsePane {
fn init(&mut self) -> Result<()> {
Expand Down Expand Up @@ -58,8 +65,14 @@ impl Pane for ResponsePane {
}

fn draw(&mut self, frame: &mut Frame<'_>, area: Rect) -> Result<()> {
frame
.render_widget(Block::default().title("Response").borders(Borders::ALL).border_style(self.border_style()), area);
frame.render_widget(
Block::default()
.title("Response")
.borders(Borders::ALL)
.border_style(self.border_style())
.border_type(self.border_type()),
area,
);
Ok(())
}
}
79 changes: 75 additions & 4 deletions src/panes/tags.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::sync::{Arc, RwLock};

use color_eyre::eyre::Result;
use crossterm::event::{KeyEvent, MouseEvent};
use ratatui::{
Expand All @@ -7,6 +9,7 @@ use ratatui::{

use crate::{
action::Action,
pages::home::State,
panes::Pane,
tui::{EventResponse, Frame},
};
Expand All @@ -15,11 +18,13 @@ use crate::{
pub struct TagsPane {
focused: bool,
focused_border_style: Style,
state: Arc<RwLock<State>>,
current_tag_index: usize,
}

impl TagsPane {
pub fn new(focused: bool, focused_border_style: Style) -> Self {
Self { focused, focused_border_style }
pub fn new(state: Arc<RwLock<State>>, focused: bool, focused_border_style: Style) -> Self {
Self { focused, focused_border_style, state, current_tag_index: 0 }
}

fn border_style(&self) -> Style {
Expand All @@ -28,6 +33,13 @@ impl TagsPane {
false => Style::default(),
}
}

fn border_type(&self) -> BorderType {
match self.focused {
true => BorderType::Thick,
false => BorderType::Plain,
}
}
}
impl Pane for TagsPane {
fn init(&mut self) -> Result<()> {
Expand All @@ -53,12 +65,71 @@ impl Pane for TagsPane {
Ok(None)
}

fn update(&mut self, _action: Action) -> Result<Option<Action>> {
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::Down => {
let state = self.state.read().unwrap();
let tags_list_len = state.openapi_spec.tags.len().saturating_add(1);
if tags_list_len > 0 {
self.current_tag_index = self.current_tag_index.saturating_add(1) % tags_list_len;
}
},
Action::Up => {
let state = self.state.read().unwrap();
let tags_list_len = state.openapi_spec.tags.len().saturating_add(1);
if tags_list_len > 0 {
self.current_tag_index = self.current_tag_index.saturating_add(tags_list_len - 1) % tags_list_len;
}
},
Action::Submit => {
let mut state = self.state.write().unwrap();
if self.current_tag_index > 0 {
if let Some(tag) = state.openapi_spec.tags.get(self.current_tag_index - 1) {
state.active_tag_name = Some(tag.name.clone());
state.active_operation_index = 0;
}
} else {
state.active_tag_name = None;
state.active_operation_index = 0;
}
return Ok(Some(Action::Update));
},
_ => {},
}

Ok(None)
}

fn draw(&mut self, frame: &mut Frame<'_>, area: Rect) -> Result<()> {
frame.render_widget(Block::default().title("Tags").borders(Borders::ALL).border_style(self.border_style()), area);
let state = self.state.read().unwrap();
let mut items: Vec<Line<'_>> = state
.openapi_spec
.tags
.iter()
.map(|tag| Line::from(vec![Span::styled(tag.name.as_str(), Style::default())]))
.collect();

items.insert(0, Line::styled("[ALL]", Style::default()));

let list = List::new(items)
.block(Block::default().borders(Borders::ALL))
.highlight_style(Style::default().add_modifier(Modifier::BOLD).bg(Color::DarkGray))
.direction(ListDirection::TopToBottom);
let mut list_state = ListState::default().with_selected(Some(self.current_tag_index));

frame.render_stateful_widget(list, area, &mut list_state);
let items_len = state.openapi_spec.tags.len() + 1;
frame.render_widget(
Block::default()
.title("Tags")
.borders(Borders::ALL)
.border_style(self.border_style())
.border_type(self.border_type())
.title_bottom(
Line::from(format!("{} of {}", self.current_tag_index.saturating_add(1), items_len)).right_aligned(),
),
area,
);
Ok(())
}
}
Binary file modified static/demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit d97476c

Please sign in to comment.