use std::{ fs, future::IntoFuture, path::{Path, PathBuf}, }; use clap::Parser; use eyre::Context; use tokio::{net::TcpListener, runtime as rt}; use tracing_subscriber::FmtSubscriber; use vnj::{app::App, cfg, db::Db, notifies, state::State}; #[derive(Debug, Parser)] pub struct Args { /// Path to the TOML config. #[clap(long, short)] pub config: PathBuf, } async fn run(cfg: cfg::Root) -> eyre::Result<()> { let listener = TcpListener::bind(cfg.http.listen) .await .wrap_err("failed to bind HTTP port")?; let db = Db::from_root(&cfg.app.journal)?; let notifies = notifies::make(); let app = App::new(db, notifies.clone())?; let state = State::new(app, cfg)?; let daemon = tokio::spawn(vnj::daemon::run(state.clone(), notifies.clone())); let router = vnj::http::make(state.clone()).with_state(state); tracing::info!("listening on {}", listener.local_addr()?); let server = tokio::spawn(axum::serve(listener, router).into_future()); tokio::try_join!(daemon, server) .wrap_err("error while running")? .0?; Ok(()) } fn read_config(path: &Path) -> eyre::Result { let contents = fs::read_to_string(path).wrap_err("failed to read")?; let cfg: cfg::Root = toml::from_str(&contents).wrap_err("failed to parse TOML")?; Ok(cfg) } fn setup_logger(level: cfg::LogLevel) -> eyre::Result<()> { let level: Option = level.into(); let subscriber = FmtSubscriber::builder(); let subscriber = if let Some(level) = level { subscriber.with_max_level(level) } else { subscriber.with_max_level(tracing::level_filters::LevelFilter::OFF) }; let subscriber = subscriber.finish(); tracing::subscriber::set_global_default(subscriber)?; Ok(()) } fn main() -> eyre::Result<()> { let rt = rt::Builder::new_current_thread() .enable_all() .thread_name("vnj") .build() .wrap_err("failed to create tokio runtime")?; let args = Args::parse(); let cfg = read_config(&args.config).wrap_err("failed to load the config")?; setup_logger(cfg.app.log_level)?; rt.block_on(run(cfg)) }