mod bootstrap;
mod compose;
mod config;
mod counter;
mod db;
mod help;
mod links;
mod nntp_client;
mod password;
mod refresh;
mod subscriptions;
mod terminal;
mod threads;
mod ui;
mod vimode;
use bootstrap::locate_paths;
use config::AppConfig;
use db as cache;
use nntp_client::NntpClient;
use password::get_password;
use std::error::Error;
use std::sync::Arc;
use subscriptions::load_subscriptions;
use terminal::{init_terminal, restore_terminal};
use tokio::sync::Mutex;
use ui::{run_ui, AppState};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let paths = locate_paths()?;
let cfg = AppConfig::load(&paths.config)?;
println!("Configuration loaded from {}: ", paths.config.display());
let password = if cfg.usenet.server.password.trim().is_empty() {
get_password(&cfg.usenet.server.username, &cfg.usenet.server.server)?
} else {
cfg.usenet.server.password.clone()
};
println!("About to connect to server... ");
let client = NntpClient::connect(
&cfg.usenet.server.server,
&cfg.usenet.server.username,
&password,
)
.await?;
let client = Arc::new(Mutex::new(client));
println!("Connection suscesfull!!!");
let conn = cache::init_db(paths.cache_db.to_str().expect("Invalid cache path"))?;
let db = Arc::new(Mutex::new(conn));
let subs = match load_subscriptions(&paths.subscriptions) {
Ok(s) if !s.is_empty() => s,
Ok(_) => {
eprintln!(
"Found '{}' but it is empty. Please list one newsgroup per line.",
paths.subscriptions.display()
);
std::process::exit(1);
}
Err(e) => {
eprintln!("Failed to open '{}': {}", paths.subscriptions.display(), e);
eprintln!(
"Create a '{}' file with one newsgroup per line.",
paths.subscriptions.display()
);
std::process::exit(1);
}
};
let mut terminal = init_terminal()?;
let app_state = AppState::new(subs, cfg.display.threaded_view);
let ui_result = run_ui(
&mut terminal,
app_state,
client,
db,
cfg.headers.from,
cfg.headers.email,
)
.await;
restore_terminal(terminal)?;
if let Err(e) = ui_result {
eprintln!("Error running UI: {}", e);
}
Ok(())
}