mod confirm_remove;
mod error_window;
mod help_window;
mod inspect;
mod loading;
mod ls;
mod removing;
mod stopping;
mod ui;
use ratatui::crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use crate::{
buildkit::{BuildKitD, container_info::SCellContainerInfo, image_info::SCellImageInfo},
cli::{
MIN_FPS,
ls::app::{
confirm_remove::ConfirmRemoveState,
error_window::ErrorWindowState,
help_window::HelpWindowState,
inspect::{InspectState, ItemToInspect},
loading::LoadingState,
ls::LsState,
removing::RemovingState,
stopping::StoppingState,
},
terminal::Terminal,
},
};
pub enum App {
Containers(AppInner<SCellContainerInfo>),
Images(AppInner<SCellImageInfo>),
}
pub trait AppItemSuperTrait: ItemToInspect {}
impl<T: ItemToInspect> AppItemSuperTrait for T {}
pub enum AppInner<Item: AppItemSuperTrait> {
Loading(LoadingState<Item>),
Ls(LsState<Item>),
HelpWindow(HelpWindowState<Item>),
Stopping(StoppingState<Item>),
ConfirmRemove(ConfirmRemoveState<Item>),
Removing(RemovingState<Item>),
ErrorWindow(ErrorWindowState<Item>),
Inspect(InspectState<Item>),
Exit,
}
impl App {
pub fn run(
buildkit: &BuildKitD,
terminal: &mut Terminal,
) -> color_eyre::Result<()> {
let mut app = Self::Containers(LoadingState::<SCellContainerInfo>::load(buildkit.clone()));
loop {
let new_app = match app {
Self::Containers(app) => app.run_one_turn()?.map(Self::Containers),
Self::Images(app) => app.run_one_turn()?.map(Self::Images),
};
let Some(new_app) = new_app else {
return Ok(());
};
app = new_app;
match &mut app {
Self::Containers(app) => {
terminal.draw(|f| {
f.render_widget(app, f.area());
})?;
},
Self::Images(app) => {
terminal.draw(|f| {
f.render_widget(app, f.area());
})?;
},
}
app = app.handle_key_event(buildkit)?;
}
}
fn handle_key_event(
mut self,
buildkit: &BuildKitD,
) -> color_eyre::Result<Self> {
if event::poll(MIN_FPS)?
&& let Event::Key(key) = event::read()?
&& key.kind == KeyEventKind::Press
{
match self {
Self::Containers(app) => {
self = app.handle_key_event(key)?.map_or_else(
|| Self::Images(LoadingState::<SCellImageInfo>::load(buildkit.clone())),
Self::Containers,
);
},
Self::Images(app) => {
self = app.handle_key_event(key)?.map_or_else(
|| {
Self::Containers(LoadingState::<SCellContainerInfo>::load(
buildkit.clone(),
))
},
Self::Images,
);
},
}
}
Ok(self)
}
}
impl<Item: Clone + AppItemSuperTrait> AppInner<Item> {
fn run_one_turn(mut self) -> color_eyre::Result<Option<Self>> {
if let Self::Loading(state) = self {
self = state.try_recv()?;
}
if let Self::Stopping(state) = self {
self = state.try_recv()?;
}
if let Self::Removing(state) = self {
self = state.try_recv()?;
}
if matches!(self, Self::Exit) {
return Ok(None);
}
Ok(Some(self))
}
}
impl AppInner<SCellContainerInfo> {
fn handle_key_event(
mut self,
key: KeyEvent,
) -> color_eyre::Result<Option<Self>> {
match key.code {
KeyCode::Char('q') => {
if let Self::Ls(_) = self {
return Ok(None);
}
},
KeyCode::Char('c' | 'd') if key.modifiers.contains(event::KeyModifiers::CONTROL) => {
self = Self::Exit;
},
KeyCode::Char('h') => {
match self {
Self::Ls(ls_state) => self = Self::HelpWindow(HelpWindowState { ls_state }),
Self::HelpWindow(state) => self = Self::Ls(state.ls_state),
_ => {},
}
},
KeyCode::Char('i') => {
match self {
Self::Ls(ls_state) => {
self = ls_state.inspect()?;
},
Self::Inspect(state) => self = Self::Ls(state.ls_state),
_ => {},
}
},
KeyCode::Down | KeyCode::Char('j') => {
if let Self::Ls(ref mut ls_state) = self {
ls_state.next();
}
if let Self::Inspect(ref mut inspect_state) = self {
inspect_state.scroll_down();
}
},
KeyCode::Up | KeyCode::Char('k') => {
if let Self::Ls(ref mut ls_state) = self {
ls_state.previous();
}
if let Self::Inspect(ref mut inspect_state) = self {
inspect_state.scroll_up();
}
},
KeyCode::Char('s') => {
if let Self::Ls(ls_state) = self {
self = ls_state.stop_selected()?;
}
},
KeyCode::Char('r') => {
if let Self::Ls(ls_state) = self {
self = Self::ConfirmRemove(ls_state.confirm_remove()?);
}
},
KeyCode::Char('y') => {
if let Self::ConfirmRemove(confirm_state) = self {
self = confirm_state.confirm();
}
},
KeyCode::Char('n') => {
if let Self::ConfirmRemove(confirm_state) = self {
self = confirm_state.cancel();
}
},
KeyCode::Esc => {
match self {
Self::HelpWindow(state) => self = Self::Ls(state.ls_state),
Self::ErrorWindow(error_state) => self = Self::Ls(error_state.ls_state),
Self::Inspect(state) => self = Self::Ls(state.ls_state),
Self::ConfirmRemove(confirm_state) => {
self = confirm_state.cancel();
},
_ => {},
}
},
_ => {},
}
Ok(Some(self))
}
}
impl AppInner<SCellImageInfo> {
fn handle_key_event(
mut self,
key: KeyEvent,
) -> color_eyre::Result<Option<Self>> {
match key.code {
KeyCode::Char('q') => {
if let Self::Ls(_) = self {
return Ok(None);
}
},
KeyCode::Char('c' | 'd') if key.modifiers.contains(event::KeyModifiers::CONTROL) => {
self = Self::Exit;
},
KeyCode::Char('h') => {
match self {
Self::Ls(ls_state) => self = Self::HelpWindow(HelpWindowState { ls_state }),
Self::HelpWindow(state) => self = Self::Ls(state.ls_state),
_ => {},
}
},
KeyCode::Char('i') => {
match self {
Self::Ls(ls_state) => {
self = ls_state.inspect()?;
},
Self::Inspect(state) => self = Self::Ls(state.ls_state),
_ => {},
}
},
KeyCode::Down | KeyCode::Char('j') => {
if let Self::Ls(ref mut ls_state) = self {
ls_state.next();
}
if let Self::Inspect(ref mut inspect_state) = self {
inspect_state.scroll_down();
}
},
KeyCode::Up | KeyCode::Char('k') => {
if let Self::Ls(ref mut ls_state) = self {
ls_state.previous();
}
if let Self::Inspect(ref mut inspect_state) = self {
inspect_state.scroll_up();
}
},
KeyCode::Char('r') => {
if let Self::Ls(ls_state) = self {
self = Self::ConfirmRemove(ls_state.confirm_remove()?);
}
},
KeyCode::Char('y') => {
if let Self::ConfirmRemove(confirm_state) = self {
self = confirm_state.confirm();
}
},
KeyCode::Char('n') => {
if let Self::ConfirmRemove(confirm_state) = self {
self = confirm_state.cancel();
}
},
KeyCode::Esc => {
match self {
Self::HelpWindow(state) => self = Self::Ls(state.ls_state),
Self::ErrorWindow(error_state) => self = Self::Ls(error_state.ls_state),
Self::Inspect(state) => self = Self::Ls(state.ls_state),
Self::ConfirmRemove(confirm_state) => {
self = confirm_state.cancel();
},
_ => {},
}
},
_ => {},
}
Ok(Some(self))
}
}