use crate::repo::RepoGroupManager; use crate::{Config, Global}; use axum::extract::FromRef; use axum::Router; use clap::Parser; use std::io; use std::path::PathBuf; use std::sync::{Arc, RwLock}; use tower_http::trace::TraceLayer; use tracing::debug; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[derive(Parser)] #[command(author, version, about, long_about = None)] pub struct Cli { /// Directory where package archives will be stored pub pkg_dir: PathBuf, /// Directory where repository metadata & SQLite database is stored pub data_dir: PathBuf, /// API key to authenticate private routes with pub api_key: String, /// Database connection URL; either sqlite:// or postgres://. Defaults to rieter.sqlite in the /// data directory #[arg(short, long)] pub database_url: Option, /// Port the server will listen on #[arg(short, long, value_name = "PORT", default_value_t = 8000)] pub port: u16, /// Log levels for the tracing #[arg( long, value_name = "LOG_LEVEL", default_value = "tower_http=debug,rieterd=debug" )] pub log: String, } impl FromRef for Arc> { fn from_ref(global: &Global) -> Self { Arc::clone(&global.repo_manager) } } impl Cli { pub fn init_tracing(&self) { tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new(self.log.clone())) .with(tracing_subscriber::fmt::layer()) .init(); } pub async fn run(&self) -> crate::Result<()> { self.init_tracing(); let db_url = if let Some(url) = &self.database_url { url.clone() } else { format!( "sqlite://{}", self.data_dir.join("rieter.sqlite").to_string_lossy() ) }; debug!("Connecting to database with URL {}", db_url); let db = crate::db::init(db_url).await?; // let db = crate::db::init("postgres://rieter:rieter@localhost:5432/rieter") // .await // .unwrap(); let config = Config { data_dir: self.data_dir.clone(), repo_dir: self.data_dir.join("repos"), pkg_dir: self.pkg_dir.clone(), api_key: self.api_key.clone(), }; let repo_manager = RepoGroupManager::new(&config.repo_dir, &self.pkg_dir); let global = Global { config, repo_manager: Arc::new(RwLock::new(repo_manager)), db, }; // build our application with a single route let app = Router::new() .nest("/api", crate::api::router()) .merge(crate::repo::router(&self.api_key)) .with_state(global) .layer(TraceLayer::new_for_http()); // run it with hyper on localhost:3000 Ok( axum::Server::bind(&format!("0.0.0.0:{}", self.port).parse().unwrap()) .serve(app.into_make_service()) .await .map_err(|err| io::Error::new(io::ErrorKind::Other, err))?, ) } }