use crossterm::event::{EventStream, KeyEvent};
use futures::{FutureExt, StreamExt};
use tokio::sync::mpsc;
use crate::tui::domain::{
SchedulerState,
executors::Executor,
jobs::{
Job, JobDetails,
stages::{JobStagesResponse, StagesGraph},
},
metrics::Metric,
};
#[derive(Clone, Debug)]
pub enum UiData {
Executors(Option<SchedulerState>, Vec<Executor>, Vec<Job>),
Metrics(Vec<Metric>),
Jobs(Vec<Job>),
JobDetails(JobDetails),
JobStagesGraph(StagesGraph),
JobStagesData(String, JobStagesResponse),
ExecutorDetails(Executor),
}
#[derive(Clone, Debug)]
#[expect(clippy::large_enum_variant)]
pub enum Event {
Key(KeyEvent),
Tick,
DataLoaded { data: UiData },
}
#[derive(Debug)]
pub struct EventHandler {
rx: mpsc::UnboundedReceiver<Event>,
}
impl EventHandler {
pub fn new(tick_rate: std::time::Duration) -> Self {
let (tx, rx) = mpsc::unbounded_channel();
tokio::spawn(async move {
let mut reader = EventStream::new();
let mut interval = tokio::time::interval(tick_rate);
loop {
let interval_delay = interval.tick();
let crossterm_event = reader.next().fuse();
tokio::select! {
_ = interval_delay => {
if tx.send(Event::Tick).is_err() {
break;
}
}
Some(Ok(evt)) = crossterm_event => {
if let crossterm::event::Event::Key(key) = evt
&& key.kind == crossterm::event::KeyEventKind::Press
&& tx.send(Event::Key(key)).is_err()
{
break;
}
}
}
}
});
Self { rx }
}
pub async fn next(&mut self) -> Option<Event> {
self.rx.recv().await
}
}