en/src/router.rs
jutty 7300a29b67 Concentrate configuration content parsing in syntax::serial
Fixes some pages not having parsed input by making it much harder to
construct a config with unparsed text, which is something you basically
never want. The parsing now happens much earlier and consumers don't
need to remember to parse the configuration anymore.

Fixes a possible stack overflow due to parsing and configuration
depending on each other.

This commit also has dependencies updates and minor justfile tweaks.
2025-12-28 06:27:30 -03:00

168 lines
4.5 KiB
Rust

use axum::{routing::get, Router};
use crate::{syntax::serial::Format, types::Graph};
mod handlers;
pub fn new(graph: &Graph) -> Router {
let mut router = Router::new()
.route(
"/",
get(|| handlers::navigation::page("index.html"))
.post(handlers::navigation::search),
)
.route(
"/static/style.css",
get(|| handlers::fixed::file("./static/style.css", "text/css")),
)
.route(
"/static/favicon.svg",
get(|| {
handlers::fixed::file("./static/favicon.svg", "image/svg+xml")
}),
)
.route(
"/node/{node_id}",
get(handlers::graph::node).post(handlers::graph::node),
)
.fallback(handlers::error::not_found);
if graph.meta.config.about {
router = router
.route("/about", get(|| handlers::navigation::page("about.html")));
}
if graph.meta.config.tree {
router = router
.route("/tree", get(|| handlers::navigation::page("tree.html")));
}
if graph.meta.config.raw {
if graph.meta.config.raw_json {
router = router.route(
"/graph/json",
get(|| handlers::fixed::serial(&Format::JSON)),
);
}
if graph.meta.config.raw_toml {
router = router.route(
"/graph/toml",
get(|| handlers::fixed::serial(&Format::TOML)),
);
}
}
router
}
#[cfg(test)]
mod tests {
use crate::{
syntax::serial::populate_graph,
types::{Config, Meta},
};
use super::*;
use axum::{
body::Body,
http::{Request, StatusCode},
response::Response,
};
use tower::ServiceExt as _;
async fn request(uri: &str, config: Option<&Config>) -> Response<Body> {
let default_graph = populate_graph();
let graph = Graph {
meta: Meta {
config: config
.map(std::borrow::ToOwned::to_owned)
.unwrap_or(default_graph.meta.config),
..default_graph.meta
},
..default_graph
};
let router = new(&graph);
router
.oneshot(Request::builder().uri(uri).body(Body::empty()).unwrap())
.await
.unwrap()
}
#[tokio::test]
async fn smoke() {
let router = axum::Router::new();
let response = router
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn routes() {
let routes = [
"/",
"/about",
"/tree",
"/node/Syntax",
"/static/style.css",
"/static/favicon.svg",
"/graph/json",
"/graph/toml",
];
for route in routes {
let response = request(route, None).await;
assert_eq!(response.status(), StatusCode::OK);
}
}
#[tokio::test]
async fn no_about_page() {
let mut config = Config::new();
config.about = false;
let response = request("/about", Some(&config)).await;
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn no_tree_page() {
let mut config = Config::new();
config.tree = false;
let response = request("/tree", Some(&config)).await;
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn no_toml_raw_graph() {
let mut config = Config::new();
config.raw_toml = false;
let response = request("/graph/toml", Some(&config)).await;
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn no_json_raw_graph() {
let mut config = Config::new();
config.raw_json = false;
let response = request("/graph/json", Some(&config)).await;
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn no_raw_graph() {
let mut config = Config::new();
config.raw = false;
let toml_response = request("/graph/toml", Some(&config)).await;
assert_eq!(toml_response.status(), StatusCode::NOT_FOUND);
let json_response = request("/graph/json", Some(&config)).await;
assert_eq!(json_response.status(), StatusCode::NOT_FOUND);
}
}