Init Commit

This commit is contained in:
2025-10-05 21:40:49 +08:00
commit 5053d19d73
8 changed files with 1419 additions and 0 deletions

55
src/app.rs Normal file
View File

@@ -0,0 +1,55 @@
use crate::event::{Event, EventHandler};
use ratatui::widgets::Block;
use ratatui::{DefaultTerminal, Frame};
use std::time::Duration;
use color_eyre::Result;
use crossterm::event::KeyCode::Char;
use ratatui::layout::Alignment;
pub(crate) struct App {
running: bool,
events: EventHandler,
}
impl App {
pub fn new() -> App {
App {
running: true,
events: EventHandler::new(Duration::from_millis(250)),
}
}
pub async fn run(&mut self, mut terminal: DefaultTerminal) -> Result<()> {
loop {
terminal.draw(|frame| App::render(self, frame))?;
let event = self.events.next().await?;
self.update(event)?;
if !self.running {
break Ok(())
}
}
}
fn update(&mut self, event: Event) -> Result<()> {
if let Event::Key(key) = event {
match key.code {
Char('q') => self.quit(),
_ => {}
}
}
Ok(())
}
fn quit(&mut self) {
self.running = false;
}
fn render(&mut self, frame: &mut Frame) {
frame.render_widget(
Block::bordered()
.title("Sus Manager")
.title_alignment(Alignment::Center),
frame.area()
);
}
}

62
src/event.rs Normal file
View File

@@ -0,0 +1,62 @@
use color_eyre::eyre::{Result, eyre};
use crossterm;
use crossterm::event;
use futures::FutureExt;
use futures::StreamExt;
use std::time::Duration;
use tokio::sync::mpsc::UnboundedSender;
use tokio::task::JoinHandle;
pub(crate) enum Event {
Error,
Tick,
Key(event::KeyEvent),
}
pub(crate) struct EventHandler {
_tx: UnboundedSender<Event>,
rx: tokio::sync::mpsc::UnboundedReceiver<Event>,
task: Option<JoinHandle<()>>,
}
impl EventHandler {
pub fn new(tick_rate: Duration) -> Self {
let mut interval = tokio::time::interval(tick_rate);
let mut event_reader = crossterm::event::EventStream::new();
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let _tx = tx.clone();
let task = tokio::spawn(async move {
loop {
let delay = interval.tick();
let crossterm_event = event_reader.next().fuse();
tokio::select! {
maybe_event = crossterm_event => {
if let Some(Err(_)) = maybe_event {
tx.send(Event::Error).unwrap()
}
else if let Some(Ok(event)) = maybe_event &&
let event::Event::Key(key) = event &&
key.kind == event::KeyEventKind::Press
{
tx.send(Event::Key(key)).unwrap()
}
}
_ = delay => {
tx.send(Event::Tick).unwrap()
}
}
}
});
Self {
_tx,
rx,
task: Some(task),
}
}
pub(crate) async fn next(&mut self) -> Result<Event> {
self.rx.recv().await.ok_or(eyre!("Unable to get event"))
}
}

15
src/main.rs Normal file
View File

@@ -0,0 +1,15 @@
mod app;
mod event;
use color_eyre::Result;
use tokio;
#[tokio::main]
async fn main() -> Result<()> {
color_eyre::install()?;
let terminal = ratatui::init();
let mut app = app::App::new();
let result = app.run(terminal).await;
ratatui::restore();
result
}