use std::sync::{Arc, Mutex}; use cron_tab::Cron; use gpio_cdev::Chip; use sea_orm::Database; use tokio::net::TcpListener; use utoipa::OpenApi; use utoipa_axum::router::OpenApiRouter; use utoipa_scalar::{Scalar, Servable}; use crate::{ringer::{BeepRinger, Ringer, SilentRinger}, scheduler::Scheduler}; mod scheduler; mod ringer; mod types; mod resources; mod dao; mod model; #[derive(Clone)] struct AppState { scheduler: Arc } fn construct_ringer() -> Arc> { let result = Chip::new("/dev/gpiochip0"); match result { Ok(chip) => { let chip = Arc::new(Mutex::new(chip)); Arc::new(Mutex::new(BeepRinger::new(chip))) as Arc> }, Err(e) => { println!("Error opening chip (falling back to silent ringer): {}", e); Arc::new(Mutex::new(SilentRinger::new())) as Arc> } } } async fn app_state() -> AppState { let cron = Arc::new(Mutex::new(Cron::new(chrono::Local))); let db = Database::connect("sqlite://snooze-pal.db").await.unwrap(); let ringer = construct_ringer(); AppState { scheduler: Arc::new(Scheduler::new( ringer, cron.clone(), Arc::new(db) )) } } #[tokio::main] async fn main() -> Result<(), Box> { let app_state = app_state().await; app_state.scheduler.start().await; start_axum_server(app_state).await; Ok(()) } #[derive(OpenApi)] struct ApiDocs; async fn start_axum_server(app_state: AppState) { let docs = ApiDocs::openapi(); let (router, spec) = OpenApiRouter::::with_openapi(docs) .nest("/v1", resources::router()) .with_state(app_state) .split_for_parts(); let router = router.merge(Scalar::with_url("/docs", spec)); let listener = TcpListener::bind("0.0.0.0:8080") .await.expect("Failed to bind to port 8080. It may be taken by another process."); axum::serve(listener, router) .await.expect("Failed to serve"); }