1use async_trait::async_trait;
6use std::collections::HashMap;
7
8#[async_trait]
31pub trait RouteResolver: Send + Sync {
32 async fn resolve(&self, key: &str) -> Option<String>;
34}
35
36pub struct StaticRouteResolver {
38 routes: HashMap<String, String>,
39}
40
41impl StaticRouteResolver {
42 pub fn new() -> Self {
44 Self {
45 routes: HashMap::new(),
46 }
47 }
48
49 pub fn add_route(mut self, key: impl Into<String>, backend_url: impl Into<String>) -> Self {
51 self.routes.insert(key.into(), backend_url.into());
52 self
53 }
54
55 pub fn with_routes(mut self, routes: HashMap<String, String>) -> Self {
57 self.routes = routes;
58 self
59 }
60}
61
62impl Default for StaticRouteResolver {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68#[async_trait]
69impl RouteResolver for StaticRouteResolver {
70 async fn resolve(&self, key: &str) -> Option<String> {
71 self.routes.get(key).cloned()
72 }
73}
74
75pub fn extract_subdomain(host: &str) -> Option<String> {
88 let host = host.split(':').next().unwrap_or(host);
90
91 let parts: Vec<&str> = host.split('.').collect();
93
94 if parts.len() >= 3 {
96 Some(parts[0].to_string())
97 } else if parts.len() == 2 {
98 None
100 } else if parts.len() == 1 && parts[0] == "localhost" {
101 Some("infra".to_string())
103 } else {
104 None
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_extract_subdomain() {
114 assert_eq!(
115 extract_subdomain("infra.example.com"),
116 Some("infra".to_string())
117 );
118 assert_eq!(
119 extract_subdomain("api.example.com"),
120 Some("api".to_string())
121 );
122 assert_eq!(extract_subdomain("example.com"), None);
123 assert_eq!(extract_subdomain("localhost"), Some("infra".to_string()));
124 assert_eq!(
125 extract_subdomain("infra.example.com:8080"),
126 Some("infra".to_string())
127 );
128 assert_eq!(
129 extract_subdomain("my-service.example.com"),
130 Some("my-service".to_string())
131 );
132 }
133
134 #[tokio::test]
135 async fn test_static_route_resolver() {
136 let resolver = StaticRouteResolver::new()
137 .add_route("api", "http://api-backend:8080")
138 .add_route("admin", "http://admin-backend:9000");
139
140 assert_eq!(
141 resolver.resolve("api").await,
142 Some("http://api-backend:8080".to_string())
143 );
144 assert_eq!(
145 resolver.resolve("admin").await,
146 Some("http://admin-backend:9000".to_string())
147 );
148 assert_eq!(resolver.resolve("unknown").await, None);
149 }
150}