Skip to content

Commit

Permalink
✨ implement compose (#66)
Browse files Browse the repository at this point in the history
♻️ refactor filters

🐛 fix inspect bug on networks and volumes
  • Loading branch information
pyaillet authored Jan 19, 2024
1 parent f0b1143 commit 00cec5f
Show file tree
Hide file tree
Showing 16 changed files with 528 additions and 103 deletions.
23 changes: 18 additions & 5 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ use ratatui::widgets::{Block, Borders, Paragraph};
use tokio::sync::mpsc::{self, UnboundedSender};

use crate::action::Action;
use crate::components::composes::Composes;
use crate::components::containers::Containers;
use crate::components::images::Images;
use crate::components::networks::Networks;
use crate::components::volumes::Volumes;
use crate::components::Component;
use crate::runtime::{get_suggestions, RuntimeSummary, CONTAINERS, IMAGES, NETWORKS, VOLUMES};
use crate::runtime::{
get_suggestions, RuntimeSummary, COMPOSES, CONTAINERS, IMAGES, NETWORKS, VOLUMES,
};
use crate::tui;
use crate::utils::{default_layout, help_screen, toast};

Expand Down Expand Up @@ -72,7 +75,7 @@ impl App {
tui.frame_rate(self.frame_rate);
tui.enter()?;

let mut main: Component = Component::Containers(Containers::new(None));
let mut main: Component = Component::Containers(Containers::new(Default::default()));
main.register_action_handler(action_tx.clone());

let info = crate::runtime::get_runtime_info().await?;
Expand Down Expand Up @@ -339,19 +342,29 @@ impl App {
match self.suggestion {
Some(CONTAINERS) => {
self.reset_input();
Some(Action::Screen(Component::Containers(Containers::new(None))))
Some(Action::Screen(Component::Containers(Containers::new(
Default::default(),
))))
}
Some(COMPOSES) => {
self.reset_input();
Some(Action::Screen(Component::Composes(Composes::new())))
}
Some(IMAGES) => {
self.reset_input();
Some(Action::Screen(Component::Images(Images::new())))
}
Some(VOLUMES) => {
self.reset_input();
Some(Action::Screen(Component::Volumes(Volumes::new())))
Some(Action::Screen(Component::Volumes(Volumes::new(
Default::default(),
))))
}
Some(NETWORKS) => {
self.reset_input();
Some(Action::Screen(Component::Networks(Networks::new())))
Some(Action::Screen(Component::Networks(Networks::new(
Default::default(),
))))
}
_ => None,
}
Expand Down
18 changes: 17 additions & 1 deletion src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use tokio::sync::mpsc::UnboundedSender;

use crate::action::Action;

use crate::components::composes::Composes;
use crate::components::container_exec::ContainerExec;
use crate::components::container_inspect::ContainerDetails;
use crate::components::container_logs::ContainerLogs;
Expand All @@ -19,6 +20,7 @@ use crate::components::volume_inspect::VolumeInspect;
use crate::components::volumes::Volumes;
use crate::tui;

pub mod composes;
pub mod container_exec;
pub mod container_inspect;
pub mod container_logs;
Expand All @@ -38,6 +40,7 @@ pub(crate) enum Component {
ContainerInspect(ContainerDetails),
ContainerLogs(ContainerLogs),
ContainerView(ContainerView),
Composes(Composes),
Images(Images),
ImageInspect(ImageInspect),
Networks(Networks),
Expand Down Expand Up @@ -81,6 +84,7 @@ impl Component {
ContainerInspect,
ContainerLogs,
ContainerView,
Composes,
Images,
ImageInspect,
Networks,
Expand All @@ -100,6 +104,7 @@ impl Component {
ContainerInspect,
ContainerLogs,
ContainerView,
Composes,
Images,
ImageInspect,
Networks,
Expand All @@ -119,6 +124,7 @@ impl Component {
ContainerInspect,
ContainerLogs,
ContainerView,
Composes,
Images,
ImageInspect,
Networks,
Expand Down Expand Up @@ -147,6 +153,7 @@ impl Component {
ContainerInspect,
ContainerLogs,
ContainerView,
Composes,
Images,
ImageInspect,
Networks,
Expand Down Expand Up @@ -179,6 +186,7 @@ impl Component {
Containers,
ContainerLogs,
ContainerView,
Composes,
Images,
Networks,
Volumes
Expand All @@ -190,7 +198,15 @@ impl Component {
pub(crate) fn get_action(&self, k: &KeyEvent) -> Option<Action> {
component_delegate!(
self.get_action(k),
[Containers, ContainerLogs, ContainerView, Images],
[
Containers,
ContainerLogs,
ContainerView,
Composes,
Images,
Networks,
Volumes
],
None
)
}
Expand Down
166 changes: 166 additions & 0 deletions src/components/composes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
use color_eyre::Result;
use crossterm::event::{self, KeyCode};
use ratatui::{
layout::{Constraint, Layout, Rect},
style::{Style, Stylize},
widgets::TableState,
Frame,
};

use tokio::sync::mpsc::UnboundedSender;

use crate::{
action::Action,
runtime::{list_compose_projects, Compose, Filter},
utils::table,
};

use super::{containers::Containers, networks::Networks, volumes::Volumes, Component};

const COMPOSES_CONSTRAINTS: [Constraint; 4] = [
Constraint::Min(20),
Constraint::Max(12),
Constraint::Max(12),
Constraint::Max(12),
];

#[derive(Clone, Debug)]
pub struct Composes {
composes: Vec<Compose>,
action_tx: Option<UnboundedSender<Action>>,
state: TableState,
}

impl Composes {
pub fn new() -> Self {
Composes {
composes: Vec::new(),
action_tx: None,
state: TableState::default(),
}
}

fn previous(&mut self) {
if !self.composes.is_empty() {
let i = match self.state.selected() {
Some(i) => {
if i == 0 {
self.composes.len() - 1
} else {
i - 1
}
}
None => 0,
};
self.state.select(Some(i));
}
}

fn next(&mut self) {
if !self.composes.is_empty() {
let i = match self.state.selected() {
Some(i) => {
if i >= self.composes.len() - 1 {
0
} else {
i + 1
}
}
None => 0,
};
self.state.select(Some(i));
}
}

fn get_selected_compose_info(&self) -> Option<String> {
self.state
.selected()
.and_then(|i| self.composes.get(i).cloned())
.map(|c| c.project)
}

pub(crate) fn get_name(&self) -> &'static str {
"Compose projects"
}

pub(crate) fn register_action_handler(&mut self, tx: UnboundedSender<Action>) {
self.action_tx = Some(tx);
}

pub(crate) async fn update(&mut self, action: Action) -> Result<()> {
let tx = self
.action_tx
.clone()
.expect("Action tx queue not initialized");
match action {
Action::Tick => {
self.composes = match list_compose_projects().await {
Ok(composes) => composes,
Err(e) => {
tx.send(Action::Error(format!(
"Error getting container list: {}",
e
)))?;
Vec::new()
}
};
self.composes.sort_by(|a, b| a.project.cmp(&b.project));
if self.state.selected().is_none() {
self.state.select(Some(0));
}
}
Action::Down => {
self.next();
}
Action::Up => {
self.previous();
}
_ => {}
}
Ok(())
}

pub(crate) fn draw(&mut self, f: &mut Frame<'_>, area: Rect) {
let rects = Layout::default()
.constraints([Constraint::Percentage(100)])
.split(area);
let t = table(
self.get_name().to_string(),
["Project", "Containers", "Volumes", "Networks"],
self.composes.iter().map(|c| c.into()).collect(),
&COMPOSES_CONSTRAINTS,
Some(Style::new().gray()),
);
f.render_stateful_widget(t, rects[0], &mut self.state);
}

pub(crate) fn get_bindings(&self) -> Option<&[(&str, &str)]> {
Some(&[
("Enter", "Compose details"),
("c", "Containers"),
("v", "Volumes"),
("n", "Networks"),
])
}

pub(crate) fn get_action(&self, k: &event::KeyEvent) -> Option<Action> {
if let Some(project) = self.get_selected_compose_info() {
let filter = Filter::default().compose_project(project);
match k.code {
KeyCode::Enter => Some(Action::Ok),
KeyCode::Char('c') => Some(Action::Screen(Component::Containers(Containers::new(
filter,
)))),
KeyCode::Char('v') => {
Some(Action::Screen(Component::Volumes(Volumes::new(filter))))
}
KeyCode::Char('n') => {
Some(Action::Screen(Component::Networks(Networks::new(filter))))
}
_ => None,
}
} else {
None
}
}
}
4 changes: 3 additions & 1 deletion src/components/container_exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ impl ContainerExec {

self.should_stop = true;
tx.send(Action::Resume)?;
tx.send(Action::Screen(Component::Containers(Containers::new(None))))?;
tx.send(Action::Screen(Component::Containers(Containers::new(
Default::default(),
))))?;
if let Err(e) = res {
tx.send(Action::Error(format!(
"Unable to execute command \"{}\" in container \"{}\"\n{}",
Expand Down
4 changes: 3 additions & 1 deletion src/components/container_inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ impl ContainerDetails {
match action {
Action::PreviousScreen => {
if let Some(tx) = self.action_tx.clone() {
tx.send(Action::Screen(Component::Containers(Containers::new(None))))?;
tx.send(Action::Screen(Component::Containers(Containers::new(
Default::default(),
))))?;
}
}
Action::Up => {
Expand Down
4 changes: 3 additions & 1 deletion src/components/container_logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ impl ContainerLogs {
match action {
Action::PreviousScreen => {
self.cancel()?;
tx.send(Action::Screen(Component::Containers(Containers::new(None))))?;
tx.send(Action::Screen(Component::Containers(Containers::new(
Default::default(),
))))?;
}
Action::Up => {
self.auto_scroll = false;
Expand Down
4 changes: 3 additions & 1 deletion src/components/container_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ impl ContainerView {
let tx = self.action_tx.clone().expect("No action sender");
match action {
Action::PreviousScreen => {
tx.send(Action::Screen(Component::Containers(Containers::new(None))))?;
tx.send(Action::Screen(Component::Containers(Containers::new(
Default::default(),
))))?;
}
Action::Tick => match get_container_details(&self.id).await {
Ok(details) => self.details = Some(details),
Expand Down
Loading

0 comments on commit 00cec5f

Please sign in to comment.