A living almanac of seasons, soil, and the quiet knowledge that used to be common. Rust axum with minijinja and Vite.
agriculturealmanacaxumfolk-knowledgegardeningminijinjarustseasonalvite
1use axum::{http::header, middleware as axum_middleware, Router};
2use minijinja::Environment;
3use std::path::PathBuf;
4use std::sync::Arc;
5use tower_http::services::ServeDir;
6use tower_http::set_header::SetResponseHeaderLayer;
7
8use crate::content::{self, SiteData};
9use crate::middleware::log_requests;
10use crate::routes;
11use crate::templates;
12
13#[derive(Clone)]
14pub struct AppState {
15 pub env: Arc<Environment<'static>>,
16 pub data: Arc<SiteData>,
17 pub dist_dir: PathBuf,
18}
19
20impl AppState {
21 pub fn from_env() -> Self {
22 let project_root: PathBuf = std::env::var("DARKFURROW_ROOT")
23 .map(PathBuf::from)
24 .unwrap_or_else(|_| PathBuf::from("."));
25
26 let templates_dir = project_root.join("templates");
27 let dist_dir = project_root.join("dist");
28 let content_dir = project_root.join("content");
29 let manifest_path = dist_dir.join(".vite/manifest.json");
30
31 let env = templates::build_env(&templates_dir, &manifest_path);
32 let data = content::load_data(&content_dir).expect("failed to load data");
33
34 Self {
35 env: Arc::new(env),
36 data: Arc::new(data),
37 dist_dir,
38 }
39 }
40}
41
42pub fn router(state: AppState) -> Router {
43 let static_files = tower::ServiceBuilder::new()
44 .layer(SetResponseHeaderLayer::if_not_present(
45 header::CACHE_CONTROL,
46 header::HeaderValue::from_static("public, max-age=31536000"),
47 ))
48 .service(ServeDir::new(&state.dist_dir));
49
50 Router::new()
51 .merge(routes::index::router())
52 .nest_service("/static", static_files)
53 .layer(axum_middleware::from_fn(log_requests))
54 .with_state(state)
55}