rust_webx_core/route/
diagnostics.rs1use std::collections::HashSet;
4
5use crate::route::scan::{HandlerCache, HandlerRegistration, RouteEntry};
6
7#[derive(Debug, Clone)]
9pub struct RouteSnapshot {
10 pub method: String,
11 pub path: &'static str,
12 pub request_type: &'static str,
13 pub response_type: &'static str,
14 pub has_handler: bool,
15}
16
17pub fn route_snapshots() -> Vec<RouteSnapshot> {
19 let cache = HandlerCache::build();
20 let mut routes: Vec<RouteSnapshot> = inventory::iter::<RouteEntry>()
21 .map(|entry| RouteSnapshot {
22 method: entry.method.as_str().to_string(),
23 path: entry.path,
24 request_type: entry.handler_type,
25 response_type: entry.rsp_type,
26 has_handler: cache.get(entry.handler_type).is_some(),
27 })
28 .collect();
29 routes.sort_by(|a, b| a.path.cmp(b.path).then(a.method.cmp(&b.method)));
30 routes
31}
32
33pub fn orphan_routes() -> Vec<&'static str> {
35 route_snapshots()
36 .into_iter()
37 .filter(|r| !r.has_handler)
38 .map(|r| r.request_type)
39 .collect()
40}
41
42pub fn orphan_handlers() -> Vec<&'static str> {
44 let routed: HashSet<&'static str> = inventory::iter::<RouteEntry>()
45 .map(|e| e.handler_type)
46 .collect();
47
48 let mut orphans = Vec::new();
49 for reg in inventory::iter::<HandlerRegistration>() {
50 if !routed.contains(reg.req_type_name) {
51 orphans.push(reg.req_type_name);
52 }
53 }
54 orphans.sort_unstable();
55 orphans
56}
57
58pub fn format_route_diagnostics() -> String {
60 use std::fmt::Write;
61
62 let routes = route_snapshots();
63 let mut out = format!("Registered HTTP routes: {}\n", routes.len());
64
65 for route in &routes {
66 let status = if route.has_handler { "ok" } else { "ORPHAN" };
67 let _ = writeln!(
68 out,
69 " [{status}] {} {} {} => {}",
70 route.method, route.path, route.request_type, route.response_type
71 );
72 }
73
74 let missing_handlers = orphan_routes();
75 if !missing_handlers.is_empty() {
76 let _ = writeln!(out, "\nRoutes without #[handler] ({}):", missing_handlers.len());
77 for req in missing_handlers {
78 let _ = writeln!(out, " - {req}");
79 }
80 }
81
82 let missing_routes = orphan_handlers();
83 if !missing_routes.is_empty() {
84 let _ = writeln!(out, "\nHandlers without route ({}):", missing_routes.len());
85 for req in missing_routes {
86 let _ = writeln!(out, " - {req}");
87 }
88 }
89
90 out
91}