use joecalsend::{Config, JoecalService, Listeners, error, models::Device}; use log::{error, info}; use ratatui::DefaultTerminal; use tokio::{sync::mpsc::unbounded_channel, task::JoinSet}; use tui_logger::{LevelFilter, init_logger, set_env_filter_from_env}; mod app; use app::{App, CurrentScreen}; fn main() -> error::Result<()> { let device = Device::default(); if std::env::var("RUST_LOG").is_err() { unsafe { std::env::set_var("RUST_LOG", "joecalsend"); } } init_logger(LevelFilter::Debug).map_err(|e| std::io::Error::other(format!("{e}")))?; set_env_filter_from_env(None); let config = Config::default(); let mut terminal = ratatui::init(); let result = start_and_run(&mut terminal, config, device); ratatui::restore(); result } #[tokio::main] async fn start_and_run( terminal: &mut DefaultTerminal, config: Config, device: Device, ) -> error::Result<()> { let (event_tx, event_listener) = unbounded_channel(); let service = JoecalService::new(device, event_tx) .await .expect("Could not create JoecalService"); let mut app = App::new(service, event_listener); let mut handles = JoinSet::new(); app.service.start(&config, &mut handles).await; loop { terminal.draw(|frame| app.draw(frame))?; app.handle_events().await?; if let Some(&top) = app.screen.last() && top == CurrentScreen::Stopping { app.service.stop().await; break; } let peers = app.service.peers.lock().await; app.peers.clear(); peers.iter().for_each(|(fingerprint, (addr, device))| { let alias = device.alias.clone(); app.peers .insert(addr.to_owned(), (alias, fingerprint.to_owned())); }); let mut stale_uploads = Vec::with_capacity(app.uploads.len()); let now = chrono::Utc::now().timestamp_millis() as u64; for (id, request) in app.uploads.iter() { if request.tx.is_closed() || (now - id.timestamp()) > 60_000 { stale_uploads.push(*id); } } for id in stale_uploads { app.uploads.remove(&id); } } shutdown(&mut handles).await; Ok(()) } async fn shutdown(handles: &mut JoinSet) { let mut alarm = tokio::time::interval(tokio::time::Duration::from_secs(5)); alarm.tick().await; loop { tokio::select! { join_result = handles.join_next() => { match join_result { Some(handle) => match handle { Ok(h) => info!("Stopped {h:?}"), Err(e) => error!("Got error {e:?}"), } None => break, } } _ = alarm.tick() => { info!("Exit timeout reached, aborting all unjoined tasks"); handles.abort_all(); break; }, } } }