diff --git a/src/main.rs b/src/main.rs index 80a31a0..8b3315c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,12 @@ mod config; mod recurring; mod state; -mod r#static; +mod web; use std::{io, path::PathBuf}; -use askama::Template; -use askama_axum::{IntoResponse, Response}; -use axum::{extract::State, http::StatusCode, routing::get, Router}; use clap::Parser; use directories::ProjectDirs; -use sqlx::SqlitePool; use state::AppState; use tokio::{select, signal::unix::SignalKind}; use tracing::{debug, info, Level}; @@ -93,22 +89,6 @@ async fn wait_for_signal() -> io::Result<()> { Ok(()) } -#[derive(Template)] -#[template(path = "index.html")] -struct IndexTemplate { - number: i32, -} - -async fn index(State(db): State) -> Result { - let result = sqlx::query!("SELECT column1 AS number FROM (VALUES (1))") - .fetch_one(&db) - .await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e}")).into_response())?; - - let number = result.number; - Ok(IndexTemplate { number }.into_response()) -} - async fn run() -> anyhow::Result<()> { let args = Args::parse(); @@ -118,20 +98,10 @@ async fn run() -> anyhow::Result<()> { let config = load_config(args.config)?; let state = AppState::new(config, &args.db, &args.repo).await?; - let app = Router::new() - .route("/", get(index)) - .fallback(get(r#static::static_handler)) - .with_state(state.clone()) - .into_make_service(); - // TODO Add text body to body-less status codes - // TODO Add anyhow-like error type for endpoints - - let server = axum::Server::bind(&"0.0.0.0:8000".parse().unwrap()); - info!("Startup complete, running"); select! { _ = wait_for_signal() => {}, - _ = server.serve(app) => {}, + _ = web::run(state.clone()) => {}, _ = recurring::run(state.clone()) => {}, } diff --git a/src/web.rs b/src/web.rs new file mode 100644 index 0000000..546d537 --- /dev/null +++ b/src/web.rs @@ -0,0 +1,53 @@ +mod index; +mod r#static; + +use std::{error, result}; + +use axum::{ + http::StatusCode, + response::{IntoResponse, Response}, + routing::get, + Router, Server, +}; + +use crate::state::AppState; + +/// Anyhow-like error that also implements [`IntoResponse`]. +pub struct Error(anyhow::Error); + +impl From for Error +where + E: error::Error + Send + Sync + 'static, +{ + fn from(value: E) -> Self { + Self(anyhow::Error::from(value)) + } +} + +impl IntoResponse for Error { + fn into_response(self) -> Response { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("500 Internal Server Error\n\n{}", self.0), + ) + .into_response() + } +} + +/// Anyhow-like result that also implements [`IntoResponse`]. +pub type Result = result::Result; + +pub async fn run(state: AppState) -> anyhow::Result<()> { + // TODO Add text body to body-less status codes + + let app = Router::new() + .route("/", get(index::get)) + .fallback(get(r#static::static_handler)) + .with_state(state.clone()); + + Server::bind(&"0.0.0.0:8000".parse().unwrap()) + .serve(app.into_make_service()) + .await?; + + Ok(()) +} diff --git a/src/web/index.rs b/src/web/index.rs new file mode 100644 index 0000000..2f402a9 --- /dev/null +++ b/src/web/index.rs @@ -0,0 +1,18 @@ +use askama::Template; +use axum::{extract::State, response::IntoResponse}; +use sqlx::SqlitePool; + +#[derive(Template)] +#[template(path = "index.html")] +struct IndexTemplate { + number: i32, +} + +pub async fn get(State(db): State) -> super::Result { + let result = sqlx::query!("SELECT column1 AS number FROM (VALUES (1))") + .fetch_one(&db) + .await?; + + let number = result.number; + Ok(IndexTemplate { number }) +} diff --git a/src/static.rs b/src/web/static.rs similarity index 100% rename from src/static.rs rename to src/web/static.rs