same functionality now works with axum the same way it did with actix

This commit is contained in:
2026-03-02 20:39:08 +01:00
parent 05ba925d54
commit a86a28fd85
7 changed files with 96 additions and 57 deletions

Binary file not shown.

View File

@@ -10,7 +10,7 @@ use utoipa::OpenApi;
use utoipa_axum::router::OpenApiRouter; use utoipa_axum::router::OpenApiRouter;
use utoipa_swagger_ui::SwaggerUi; use utoipa_swagger_ui::SwaggerUi;
use crate::{ringer::BeepRinger, scheduler::Scheduler}; use crate::{ringer::{BeepRinger, SilentRinger}, scheduler::Scheduler};
mod scheduler; mod scheduler;
mod ringer; mod ringer;
@@ -22,16 +22,17 @@ mod model;
#[derive(Clone)] #[derive(Clone)]
struct AppState { struct AppState {
scheduler: Arc<Scheduler<BeepRinger>> scheduler: Arc<Scheduler<SilentRinger>>
} }
async fn app_state() -> AppState { async fn app_state() -> AppState {
let chip: Arc<Mutex<Chip>> = Arc::new(Mutex::new(Chip::new("/dev/gpiochip0").unwrap())); // let chip: Arc<Mutex<Chip>> = Arc::new(Mutex::new(Chip::new("/dev/gpiochip0").unwrap()));
let cron = Arc::new(Mutex::new(Cron::new(chrono::Local))); let cron = Arc::new(Mutex::new(Cron::new(chrono::Local)));
let db = Database::connect("sqlite://snooze-pal.db").await.unwrap(); let db = Database::connect("sqlite://snooze-pal.db").await.unwrap();
let silent = Arc::new(Mutex::new(SilentRinger::new()));
AppState { AppState {
scheduler: Arc::new(Scheduler::new( scheduler: Arc::new(Scheduler::new(
Arc::new(Mutex::new(BeepRinger::new(chip.clone()))), silent,
cron.clone(), cron.clone(),
Arc::new(db) Arc::new(db)
)) ))
@@ -40,11 +41,9 @@ async fn app_state() -> AppState {
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
// let app_state = app_state().await; let app_state = app_state().await;
app_state.scheduler.start();
// app_state.scheduler.start(); start_axum_server(app_state).await;
start_axum_server().await;
Ok(()) Ok(())
} }
@@ -52,11 +51,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
#[derive(OpenApi)] #[derive(OpenApi)]
struct ApiDocs; struct ApiDocs;
async fn start_axum_server() { async fn start_axum_server(app_state: AppState) {
let docs = ApiDocs::openapi(); let docs = ApiDocs::openapi();
let (router, spec) = OpenApiRouter::with_openapi(docs) let (router, spec) = OpenApiRouter::<AppState>::with_openapi(docs)
.nest("/v1", resources::router()) .nest("/v1", resources::router())
.with_state(app_state)
.split_for_parts(); .split_for_parts();
let router = router.merge(SwaggerUi::new("/swagger-ui").url("/openapi.json", spec)); let router = router.merge(SwaggerUi::new("/swagger-ui").url("/openapi.json", spec));

View File

@@ -1,7 +1,8 @@
mod post; mod post;
use utoipa_axum::{router::OpenApiRouter, routes}; use utoipa_axum::{router::OpenApiRouter, routes};
pub fn router() -> OpenApiRouter { use crate::AppState;
OpenApiRouter::new()
.routes(routes!(post::post_handler)) pub fn router() -> OpenApiRouter<AppState> {
OpenApiRouter::new().routes(routes!(post::post_handler))
} }

View File

@@ -1,32 +1,51 @@
use axum::Json; use axum::extract::State;
use axum::{Json};
use axum::http::StatusCode; use axum::http::StatusCode;
use axum::debug_handler; use axum::debug_handler;
use axum::response::IntoResponse; use axum::response::IntoResponse;
use chrono::{DateTime, Local}; use chrono::{DateTime, Local};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::{Deserialize, Serialize};
use utoipa::{IntoResponses, ToSchema}; use utoipa::{IntoResponses, ToSchema};
use crate::AppState;
use crate::types::Alarm;
#[derive(Debug, Deserialize, JsonSchema, ToSchema)] #[derive(Debug, Deserialize, JsonSchema, ToSchema)]
pub struct RequestBody { pub struct RequestBody {
time: DateTime<Local>, time: DateTime<Local>,
} }
#[derive(ToSchema, Serialize)]
pub struct OkResponseBody {
time: DateTime<Local>,
enabled: bool,
}
#[derive(IntoResponses)] #[derive(IntoResponses)]
pub enum Responses { pub enum Responses {
#[response(status = 200)] #[response(status = 200)]
Ok Ok(#[to_schema] OkResponseBody),
#[response(status = 500)]
Error(String)
} }
impl IntoResponse for Responses { impl IntoResponse for Responses {
fn into_response(self) -> axum::response::Response { fn into_response(self) -> axum::response::Response {
match self { match self {
Responses::Ok => (StatusCode::OK).into_response(), Responses::Ok(body) => (StatusCode::OK, Json(body)).into_response(),
Responses::Error(message) => (StatusCode::INTERNAL_SERVER_ERROR, Json(message)).into_response(),
} }
} }
} }
impl From<Alarm> for OkResponseBody {
fn from(value: Alarm) -> Self {
OkResponseBody { time: value.time, enabled: value.enabled }
}
}
#[utoipa::path( #[utoipa::path(
post, post,
path = "", path = "",
@@ -35,6 +54,7 @@ impl IntoResponse for Responses {
) )
)] )]
#[debug_handler] #[debug_handler]
pub async fn post_handler(Json(body): Json<RequestBody>) -> Responses { pub async fn post_handler(State(AppState { scheduler, ..}): State<AppState>, Json(body): Json<RequestBody>) -> Responses {
todo!() let alarm = scheduler.add_alarm(body.time).await.unwrap();
Responses::Ok(alarm.into())
} }

View File

@@ -1,8 +1,9 @@
use utoipa_axum::router::OpenApiRouter; use utoipa_axum::router::OpenApiRouter;
use crate::AppState;
mod alarm; mod alarm;
pub fn router() -> OpenApiRouter { pub fn router() -> OpenApiRouter<AppState> {
OpenApiRouter::new() OpenApiRouter::new().nest("/alarm", alarm::router())
.nest("/alarm", alarm::router())
} }

View File

@@ -1,4 +1,7 @@
use std::{sync::{Arc, Mutex}, thread}; use std::{
sync::{Arc, Mutex},
thread,
};
use gpio_cdev::{Chip, LineRequestFlags}; use gpio_cdev::{Chip, LineRequestFlags};
@@ -12,7 +15,6 @@ pub struct BeepRinger {
} }
impl BeepRinger { impl BeepRinger {
fn beep(times: u32, chip: &mut Chip) -> Result<(), String> { fn beep(times: u32, chip: &mut Chip) -> Result<(), String> {
let beeper = chip.get_line(17); let beeper = chip.get_line(17);
@@ -24,9 +26,7 @@ impl BeepRinger {
} }
}; };
let beeper = beeper.request(LineRequestFlags::OUTPUT, 0, "my-gpio");
let beeper = beeper
.request(LineRequestFlags::OUTPUT, 0, "my-gpio");
let beeper = match beeper { let beeper = match beeper {
Ok(beeper) => beeper, Ok(beeper) => beeper,
@@ -68,3 +68,20 @@ impl Ringer for BeepRinger {
Ok(()) Ok(())
} }
} }
/// Used for local testing without an actual beeper or similar. The only thing it does is print
/// that it's ringing.
pub struct SilentRinger;
impl SilentRinger {
pub fn new() -> Self {
Self {}
}
}
impl Ringer for SilentRinger {
fn ring(&self) -> Result<(), String> {
println!("Ringing");
Ok(())
}
}

View File

@@ -45,7 +45,7 @@ impl<T: Ringer> Scheduler<T> {
}); });
job_result.expect("Faild to add job"); job_result.expect("Faild to add job");
todo!() Ok(())
} }
pub async fn add_alarm(&self, time: DateTime<Local>) -> Result<Alarm, String> { pub async fn add_alarm(&self, time: DateTime<Local>) -> Result<Alarm, String> {