Files
sops-manager/src/app.rs

129 lines
4.5 KiB
Rust

use color_eyre::Report;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::{prelude::CrosstermBackend, widgets::ListState, DefaultTerminal, Terminal};
use ratatui_form::{Email, Form};
use std::{io, path::Path};
use crate::{
event::{AppEvent, Event, EventHandler},
form::FormState,
manifest::{load_manifest, Manifest},
};
// #[derive(Debug)]
pub struct App {
pub running: bool,
pub list_state: ListState,
pub events: EventHandler,
pub isloadingmanifest: bool,
pub manifest: Option<Manifest>,
}
impl App {
pub fn new() -> Self {
Self {
running: true,
list_state: ListState::default().with_selected(Some(0)),
events: EventHandler::new(),
isloadingmanifest: false,
manifest: None,
}
}
pub async fn run(
mut self,
terminal: &mut Terminal<CrosstermBackend<io::Stdout>>,
) -> color_eyre::Result<()> {
let project_root = Path::new("/home/nikkuss/dotfiles-new");
self.events.send(AppEvent::LoadManifest);
while self.running {
terminal.draw(|frame| frame.render_widget(&mut self, frame.area()))?;
match self.events.next().await? {
Event::Tick => self.tick(),
Event::Crossterm(event) => match event {
crossterm::event::Event::Key(key_event)
if key_event.kind == crossterm::event::KeyEventKind::Press =>
{
self.handle_key_events(key_event)?
}
_ => {}
},
Event::App(app_event) => match app_event {
AppEvent::LoadManifest => {
if !self.isloadingmanifest {
let tx = self.events.clone_sender();
tokio::spawn(async move {
let result = load_manifest(&project_root).await;
let _ = tx.send(Event::App(AppEvent::ManifestLoaded(result)));
});
}
}
AppEvent::ManifestLoaded(manifest) => {
self.isloadingmanifest = false;
match manifest {
Ok(m) => {
self.manifest = Some(m);
}
Err(e) => {
self.display_error(e);
}
}
}
AppEvent::SelectionUp => self.selection_up(),
AppEvent::SelectionDown => self.selection_down(),
AppEvent::Quit => self.quit(),
},
}
}
Ok(())
}
/// Handles the key events and updates the state of [`App`].
pub fn handle_key_events(&mut self, key_event: KeyEvent) -> color_eyre::Result<()> {
match key_event.code {
KeyCode::Esc | KeyCode::Char('q') => self.events.send(AppEvent::Quit),
KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => {
self.events.send(AppEvent::Quit)
}
KeyCode::Up => self.events.send(AppEvent::SelectionUp),
KeyCode::Down => self.events.send(AppEvent::SelectionDown),
KeyCode::Char('a') => self.create_secret(),
// Other handlers you could add here.
_ => {}
}
Ok(())
}
pub fn selected_secret_name(&self) -> Option<String> {
let selected = self.list_state.selected()?;
if let Some(manifest) = &self.manifest {
return manifest.secrets.keys().nth(selected).cloned();
}
None
}
/// Handles the tick event of the terminal.
///
/// The tick event is where you can update the state of your application with any logic that
/// needs to be updated at a fixed frame rate. E.g. polling a server, updating an animation.
pub fn tick(&self) {}
/// Set running to false to quit the application.
pub fn quit(&mut self) {
self.running = false;
}
pub fn selection_down(&mut self) {
self.list_state.select_next();
}
pub fn selection_up(&mut self) {
self.list_state.select_previous();
}
pub fn display_error(&mut self, display_error: Report) {
// somehow display the error?
}
pub fn create_secret(&mut self) {}
}