mod content; use anyhow::{anyhow, Result}; use axum::{ extract::State, http::StatusCode, response::{IntoResponse, Response}, routing::get, Router, Server, }; use figment::{ providers::{Env, Format, Serialized, Toml}, Figment, }; use serde::{Deserialize, Serialize}; use sqlx::{postgres::PgPoolOptions, Pool, Postgres}; use std::{net::SocketAddr, sync::Arc}; use crate::content::Page; #[derive(Deserialize, Serialize)] struct Config { bind: String, database_url: String, } impl Default for Config { fn default() -> Self { Config { bind: "127.0.0.1:3000".to_owned(), database_url: "postgres://artificiale:changeme@localhost/artificiale".to_owned(), } } } struct GenericError(anyhow::Error); impl IntoResponse for GenericError { fn into_response(self) -> Response { ( StatusCode::INTERNAL_SERVER_ERROR, format!("Something went wrong: {}", self.0), ) .into_response() } } impl From for GenericError { fn from(value: sqlx::Error) -> Self { GenericError(anyhow!("Database error: {}", value)) } } struct AppState { database: Pool, } async fn root(State(state): State>) -> Result { let select_query: sqlx::query::QueryAs<'_, _, Page, _> = sqlx::query_as::<_, Page>("SELECT * FROM pages"); let page: Page = select_query.fetch_one(&state.database).await?; Ok(page.title) } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let config: Config = Figment::from(Serialized::defaults(Config::default())) .merge(Toml::file("mabel.toml")) .merge(Env::prefixed("MABEL_")) .extract()?; let pool = PgPoolOptions::new() .max_connections(5) .connect(config.database_url.as_str()) .await?; sqlx::migrate!().run(&pool).await?; let shared_state = Arc::new(AppState { database: pool }); let app = Router::new().route("/", get(root)).with_state(shared_state); let addr: SocketAddr = config.bind.parse()?; tracing::debug!("listening on {}", addr); Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); Ok(()) }