2024-05-07 21:13:46 -05:00
|
|
|
use axum::extract::State;
|
|
|
|
use axum::response::IntoResponse;
|
|
|
|
use axum::routing::get;
|
|
|
|
use axum::{http::StatusCode, response::Html, serve, Router};
|
2024-05-13 17:53:23 -05:00
|
|
|
use minijinja::context;
|
2024-05-14 12:28:27 -05:00
|
|
|
use state::State as AppState;
|
2024-05-12 20:27:13 -05:00
|
|
|
pub use tracing::{debug, error, event, info, span, warn, Level};
|
2024-05-05 11:22:54 -05:00
|
|
|
|
2024-05-14 12:28:27 -05:00
|
|
|
use crate::live_reload::live_reload_layer;
|
|
|
|
|
2024-05-07 21:13:46 -05:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct Berr(Box<dyn std::error::Error>);
|
|
|
|
type Besult<T> = std::result::Result<T, Berr>;
|
2024-05-06 13:05:04 -05:00
|
|
|
|
2024-05-14 12:28:27 -05:00
|
|
|
mod file_watcher;
|
|
|
|
mod live_reload;
|
2024-05-13 17:53:23 -05:00
|
|
|
mod observe;
|
2024-05-14 12:28:27 -05:00
|
|
|
mod state;
|
|
|
|
mod static_files;
|
2024-05-13 17:53:23 -05:00
|
|
|
mod tailwind;
|
|
|
|
mod templates;
|
2024-05-06 13:05:04 -05:00
|
|
|
|
2024-05-06 15:39:21 -05:00
|
|
|
#[tokio::main]
|
|
|
|
async fn main() -> Besult<()> {
|
|
|
|
// load configuration?
|
2024-05-13 17:53:23 -05:00
|
|
|
let _setup_logging = observe::setup_logging();
|
2024-05-05 11:22:54 -05:00
|
|
|
|
2024-05-14 12:28:27 -05:00
|
|
|
let state = AppState::try_new().await?;
|
2024-05-07 21:13:46 -05:00
|
|
|
|
2024-05-13 17:53:23 -05:00
|
|
|
// TODO: only start tailwind if in dev mode?
|
2024-05-14 12:28:27 -05:00
|
|
|
tokio::spawn(async move { tailwind::start_watcher() });
|
2024-05-07 21:13:46 -05:00
|
|
|
|
2024-05-14 12:28:27 -05:00
|
|
|
let lr = live_reload_layer();
|
|
|
|
|
|
|
|
{
|
|
|
|
// TODO: only start watcher for dev mode?
|
|
|
|
let state = state.clone();
|
|
|
|
let lr = lr.reloader();
|
|
|
|
tokio::spawn(async move { state.templates.start_watcher(Some(lr)) });
|
|
|
|
}
|
|
|
|
|
|
|
|
let (static_file_service, _static_file_watcher) = static_files::router(Some(lr.reloader()))?;
|
2024-05-05 11:22:54 -05:00
|
|
|
|
2024-05-06 15:39:21 -05:00
|
|
|
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
|
2024-05-14 12:28:27 -05:00
|
|
|
|
|
|
|
info!("Listening on {listener:?}");
|
2024-05-07 19:49:04 -05:00
|
|
|
let router = Router::new()
|
2024-05-07 21:13:46 -05:00
|
|
|
.route("/", get(index))
|
2024-05-14 12:28:27 -05:00
|
|
|
.nest_service("/static", static_file_service)
|
|
|
|
.with_state(state);
|
2024-05-07 19:49:04 -05:00
|
|
|
|
|
|
|
Ok(serve(listener, router).await?)
|
2024-05-05 11:22:54 -05:00
|
|
|
}
|
|
|
|
|
2024-05-07 21:13:46 -05:00
|
|
|
async fn index(State(state): State<AppState>) -> Besult<Html<String>> {
|
|
|
|
Ok(Html(
|
|
|
|
state
|
|
|
|
.templates
|
2024-05-13 17:53:23 -05:00
|
|
|
.render("pages/index.html.jinja", context!())
|
|
|
|
.await?,
|
2024-05-07 21:13:46 -05:00
|
|
|
))
|
|
|
|
}
|
|
|
|
|
|
|
|
impl IntoResponse for Berr {
|
|
|
|
fn into_response(self) -> axum::http::Response<axum::body::Body> {
|
|
|
|
error!("webserver error: {:?}", self.0);
|
|
|
|
(
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
|
|
format!("internal server error: {}", self.0),
|
|
|
|
)
|
|
|
|
.into_response()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This enables using `?` on functions that return `Result<_, anyhow::Error>`
|
|
|
|
// to turn them into `Result<_, AppError>`. That way you don't need to do that
|
|
|
|
// manually.
|
|
|
|
impl<E> From<E> for Berr
|
|
|
|
where
|
|
|
|
E: Into<Box<dyn std::error::Error>>,
|
|
|
|
{
|
|
|
|
fn from(err: E) -> Self {
|
|
|
|
Self(err.into())
|
|
|
|
}
|
|
|
|
}
|