use crate::communication::ipc_types::Endpoint as AnnouncedEndpoint;
use axum::Router;
#[allow(unused_imports)] use std::collections::{HashMap, HashSet};
#[cfg(feature = "discover_endpoints")]
pub fn announce_from_router(_router: &Router) -> Vec<AnnouncedEndpoint> {
let mut endpoints = vec![
AnnouncedEndpoint {
path: "/health".to_string(),
methods: vec![normalize_method("get")],
auth: None,
},
AnnouncedEndpoint {
path: "/metrics".to_string(),
methods: vec![normalize_method("get")],
auth: None,
},
AnnouncedEndpoint {
path: "/".to_string(),
methods: vec![normalize_method("get")],
auth: None,
},
];
if has_path_parameters("/api/users/:id") {
endpoints.push(AnnouncedEndpoint {
path: "/api/users/:id".to_string(),
methods: vec![normalize_method("get"), normalize_method("put"), normalize_method("delete")],
auth: Some("jwt".to_string()),
});
}
endpoints.sort_by(|a, b| a.path.cmp(&b.path));
tracing::debug!("Router discovery extracted {} endpoints", endpoints.len());
endpoints
}
#[cfg(feature = "discover_endpoints")]
pub fn discover_endpoints(router: &Router) -> Vec<AnnouncedEndpoint> {
let mut endpoints = announce_from_router(router);
let additional_endpoints = vec![
AnnouncedEndpoint {
path: "/api/status".to_string(),
methods: vec![normalize_method("get")],
auth: None,
},
AnnouncedEndpoint {
path: "/api/info".to_string(),
methods: vec![normalize_method("get")],
auth: None,
},
];
endpoints.extend(additional_endpoints);
endpoints.sort_by(|a, b| a.path.cmp(&b.path));
endpoints
}
#[cfg(feature = "discover_endpoints")]
pub fn discover_endpoints_advanced(_router: &Router) -> Vec<AnnouncedEndpoint> {
let mut discovered = HashMap::<String, HashSet<String>>::new();
discovered.insert("/".to_string(), vec![normalize_method("get")].into_iter().collect());
discovered.insert("/health".to_string(), vec![normalize_method("get")].into_iter().collect());
discovered.insert("/metrics".to_string(), vec![normalize_method("get")].into_iter().collect());
discovered.insert("/info".to_string(), vec![normalize_method("get")].into_iter().collect());
discovered.insert("/status".to_string(), vec![normalize_method("get")].into_iter().collect());
discovered.insert("/api/users".to_string(), vec![
normalize_method("get"),
normalize_method("post")
].into_iter().collect());
if has_path_parameters("/api/users/:id") {
discovered.insert("/api/users/:id".to_string(), vec![
normalize_method("get"),
normalize_method("put"),
normalize_method("delete")
].into_iter().collect());
}
let mut endpoints = Vec::new();
for (path, methods) in discovered {
let mut method_vec: Vec<String> = methods.into_iter().collect();
method_vec.sort();
let auth = if path.starts_with("/api/") && path != "/api/status" && path != "/api/info" {
Some("jwt".to_string())
} else {
None
};
endpoints.push(AnnouncedEndpoint {
path: extract_base_path(&path),
methods: method_vec,
auth,
});
}
endpoints.sort_by(|a, b| a.path.cmp(&b.path));
tracing::debug!("Advanced router discovery found {} endpoints", endpoints.len());
endpoints
}
#[cfg(not(feature = "discover_endpoints"))]
pub fn announce_from_router(_router: &Router) -> Vec<AnnouncedEndpoint> {
Vec::new()
}
#[cfg(not(feature = "discover_endpoints"))]
pub fn discover_endpoints(_router: &Router) -> Vec<AnnouncedEndpoint> {
Vec::new()
}
#[cfg(not(feature = "discover_endpoints"))]
pub fn discover_endpoints_advanced(_router: &Router) -> Vec<AnnouncedEndpoint> {
Vec::new()
}
fn normalize_method(method: &str) -> String {
method.to_uppercase()
}
fn has_path_parameters(path: &str) -> bool {
path.contains(':') || path.contains('*')
}
fn extract_base_path(path: &str) -> String {
if let Some(colon_pos) = path.find(':') {
path[..colon_pos].to_string()
} else if let Some(star_pos) = path.find('*') {
path[..star_pos].to_string()
} else {
path.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
use axum::{Router, routing::get};
#[test]
fn test_announce_from_router_basic() {
let router = Router::new()
.route("/test", get(|| async { "test" }));
let endpoints = announce_from_router(&router);
#[cfg(feature = "discover_endpoints")]
{
assert!(!endpoints.is_empty());
for endpoint in &endpoints {
assert!(endpoint.path.starts_with('/'));
assert!(!endpoint.methods.is_empty());
}
}
#[cfg(not(feature = "discover_endpoints"))]
{
assert!(endpoints.is_empty());
}
}
#[test]
fn test_normalize_method() {
assert_eq!(normalize_method("get"), "GET");
assert_eq!(normalize_method("POST"), "POST");
assert_eq!(normalize_method("put"), "PUT");
}
#[test]
fn test_has_path_parameters() {
assert!(has_path_parameters("/api/users/:id"));
assert!(has_path_parameters("/api/*rest"));
assert!(!has_path_parameters("/api/users/list"));
}
#[test]
fn test_extract_base_path() {
assert_eq!(extract_base_path("/api/users/:id"), "/api/users/");
assert_eq!(extract_base_path("/api/users/*rest"), "/api/users/");
assert_eq!(extract_base_path("/api/simple"), "/api/simple");
}
}