mod browser;
mod input;
use cocotte::eyre::Result;
use crossterm::event::{Event, EventStream, KeyCode, KeyEvent, KeyModifiers};
use dirs::home_dir;
use futures::StreamExt;
use std::path::PathBuf;
use std::time::Duration;
#[derive(Debug, Clone)]
pub enum EventEnum {
Tick,
Setup,
Key(KeyEvent),
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum Focus {
#[default]
Directories,
Files,
}
#[derive(Default)]
pub struct AppState {
pub directory_filter: String,
pub file_filter: String,
pub focus: Focus,
pub current_directory: PathBuf,
}
impl AppState {
pub fn new() -> Self {
Self {
current_directory: home_dir().unwrap_or_default(),
..Default::default()
}
}
}
struct EventPump {
reader: EventStream,
interval: tokio::time::Interval,
}
impl EventPump {
fn new(fps: f32) -> Self {
let period = Duration::from_secs_f32(1.0 / fps);
Self {
reader: EventStream::new(),
interval: tokio::time::interval(period),
}
}
async fn next(&mut self) -> Option<EventEnum> {
tokio::select! {
_ = self.interval.tick() => Some(EventEnum::Tick),
Some(Ok(ev)) = self.reader.next() => {
if let Event::Key(key) = ev {
Some(EventEnum::Key(key))
} else {
None
}
}
}
}
}
cocotte::define_sub_apps! {
event = EventEnum;
state = AppState;
Input(input::Input) => input::Input::new(),
Browser(browser::Browser) => browser::Browser::new(),
}
#[tokio::main]
async fn main() -> Result<()> {
let mut event_pump = EventPump::new(2.0);
let mut app = make_app()?;
let mut app_state = AppState::new();
app.handle_input(&mut EventEnum::Setup, &mut app_state);
while let Some(mut app_event) = event_pump.next().await {
if let EventEnum::Key(key_event) = &app_event
&& key_event.modifiers.contains(KeyModifiers::CONTROL)
&& (key_event.code == KeyCode::Char('c') || key_event.code == KeyCode::Char('d'))
{
break;
}
app.handle_input(&mut app_event, &mut app_state);
app.draw(&mut app_state)?;
}
Ok(())
}