138 lines
4.2 KiB
Rust
138 lines
4.2 KiB
Rust
use std::{path::Path, str::FromStr, time::Duration};
|
|
|
|
use clap::Parser;
|
|
use jocalsend::{Config, DEFAULT_INTERVAL, JocalService, JocalTasks, error::Result};
|
|
use log::{error, info};
|
|
use ratatui::DefaultTerminal;
|
|
use ratatui_explorer::FileExplorer;
|
|
use tokio::task::JoinSet;
|
|
use tui_logger::{LevelFilter, init_logger, set_env_filter_from_env};
|
|
|
|
mod app;
|
|
use app::{App, CurrentScreen, Peer};
|
|
|
|
mod cli;
|
|
use cli::Cli;
|
|
|
|
fn main() -> Result<()> {
|
|
// just in case we need to display the help
|
|
let _ = Cli::parse();
|
|
|
|
if std::env::var("RUST_LOG").is_err() {
|
|
unsafe {
|
|
std::env::set_var("RUST_LOG", "jocalsend");
|
|
}
|
|
}
|
|
init_logger(LevelFilter::Info).map_err(|e| std::io::Error::other(format!("{e}")))?;
|
|
set_env_filter_from_env(None);
|
|
|
|
let config = Config::new()?;
|
|
|
|
let mut terminal = ratatui::init();
|
|
let result = start_and_run(&mut terminal, config);
|
|
ratatui::restore();
|
|
|
|
result
|
|
}
|
|
|
|
#[tokio::main(flavor = "multi_thread")]
|
|
async fn start_and_run(terminal: &mut DefaultTerminal, config: Config) -> Result<()> {
|
|
let (service, event_listener) = JocalService::new(config.clone()).await?;
|
|
|
|
let mut app = App::new(service, event_listener);
|
|
|
|
let cli = Cli::parse();
|
|
if let Some(text) = cli.text {
|
|
let i = app.input();
|
|
*i = i.clone().with_value(text.clone());
|
|
app.text().replace(text);
|
|
}
|
|
if let Some(file) = cli.file {
|
|
let path = std::path::PathBuf::from_str(&file.to_string_lossy())
|
|
.unwrap_or_else(|_| panic!("Could not create path from {file:?}"));
|
|
set_file_selection(&path, app.files());
|
|
}
|
|
|
|
let mut handles = JoinSet::new();
|
|
app.service.start(&mut handles).await;
|
|
let shutdown = shutdown(&mut handles);
|
|
let mut shutdown = std::pin::pin!(shutdown);
|
|
loop {
|
|
terminal.draw(|frame| app.draw(frame))?;
|
|
|
|
if app.screen() == CurrentScreen::Stopping {
|
|
tokio::select! {
|
|
_ = shutdown.as_mut() => {
|
|
break;
|
|
}
|
|
_ = tokio::time::sleep(DEFAULT_INTERVAL) => {}
|
|
}
|
|
} else {
|
|
app.handle_events().await?;
|
|
|
|
let peers = app.service.peers.lock().await;
|
|
app.peers.clear();
|
|
peers.iter().for_each(|(fingerprint, (addr, device))| {
|
|
let alias = device.alias.clone();
|
|
let peer = Peer {
|
|
alias,
|
|
fingerprint: fingerprint.to_owned(),
|
|
addr: addr.to_owned(),
|
|
};
|
|
app.peers.push(peer);
|
|
});
|
|
|
|
let mut stale_rx_requests = Vec::with_capacity(app.receive_requests.len());
|
|
let now = chrono::Utc::now().timestamp_millis() as u64;
|
|
for (id, request) in app.receive_requests.iter() {
|
|
if request.tx.is_closed() || (now - id.timestamp()) > 60_000 {
|
|
stale_rx_requests.push(*id);
|
|
}
|
|
}
|
|
for id in stale_rx_requests {
|
|
app.receive_requests.remove(&id);
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn shutdown(handles: &mut JoinSet<JocalTasks>) {
|
|
let mut timeout = tokio::time::interval(Duration::from_secs(5));
|
|
timeout.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,
|
|
}
|
|
}
|
|
_ = timeout.tick() => {
|
|
info!("Exit timeout reached, aborting all unjoined tasks");
|
|
handles.abort_all();
|
|
break;
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
fn set_file_selection(path: &Path, explorer: &mut FileExplorer) {
|
|
let parent = path.parent().map(|f| f.to_path_buf()).unwrap_or("/".into());
|
|
let _ = explorer.set_cwd(parent);
|
|
let files = explorer.files();
|
|
let mut idx = None;
|
|
for (i, f) in files.iter().enumerate() {
|
|
if f.name() == path.file_name().unwrap().to_string_lossy() {
|
|
idx = Some(i);
|
|
break;
|
|
}
|
|
}
|
|
if let Some(idx) = idx {
|
|
explorer.set_selected_idx(idx);
|
|
}
|
|
}
|