1use std::net::SocketAddr;
4
5pub fn create_socket_addr(host: &str, port: u16) -> Result<SocketAddr, String> {
7 format!("{}:{}", host, port)
8 .parse()
9 .map_err(|e| format!("Invalid socket address {}:{}: {}", host, port, e))
10}
11
12pub fn localhost_socket_addr(port: u16) -> SocketAddr {
14 SocketAddr::from(([127, 0, 0, 1], port))
15}
16
17pub fn wildcard_socket_addr(port: u16) -> SocketAddr {
19 SocketAddr::from(([0, 0, 0, 0], port))
20}
21
22#[derive(Debug, Clone)]
24pub struct ServerConfig {
25 pub host: String,
26 pub port: u16,
27 pub server_type: ServerType,
28}
29
30#[derive(Debug, Clone)]
31pub enum ServerType {
32 HTTP,
33 WebSocket,
34 GRPC,
35}
36
37impl ServerConfig {
38 pub fn new(host: String, port: u16, server_type: ServerType) -> Self {
40 Self {
41 host,
42 port,
43 server_type,
44 }
45 }
46
47 pub fn http(port: u16) -> Self {
49 Self::new("0.0.0.0".to_string(), port, ServerType::HTTP)
50 }
51
52 pub fn websocket(port: u16) -> Self {
54 Self::new("0.0.0.0".to_string(), port, ServerType::WebSocket)
55 }
56
57 pub fn grpc(port: u16) -> Self {
59 Self::new("0.0.0.0".to_string(), port, ServerType::GRPC)
60 }
61
62 pub fn socket_addr(&self) -> Result<SocketAddr, String> {
64 create_socket_addr(&self.host, self.port)
65 }
66
67 pub fn description(&self) -> String {
69 match self.server_type {
70 ServerType::HTTP => format!("HTTP server on {}:{}", self.host, self.port),
71 ServerType::WebSocket => format!("WebSocket server on {}:{}", self.host, self.port),
72 ServerType::GRPC => format!("gRPC server on {}:{}", self.host, self.port),
73 }
74 }
75}
76
77pub trait ServerStarter {
79 fn server_type(&self) -> ServerType;
81
82 fn port(&self) -> u16;
84
85 fn start_server(
87 self,
88 ) -> impl std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send;
89}
90
91pub async fn start_server<S: ServerStarter>(
93 server: S,
94) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
95 let port = server.port();
96 let server_type = server.server_type();
97
98 match server_type {
99 ServerType::HTTP => tracing::info!("HTTP listening on port {}", port),
100 ServerType::WebSocket => tracing::info!("WebSocket listening on port {}", port),
101 ServerType::GRPC => tracing::info!("gRPC listening on port {}", port),
102 }
103
104 server.start_server().await
105}
106
107pub mod health {
109 use serde::{Deserialize, Serialize};
110
111 #[derive(Debug, Serialize, Deserialize)]
112 pub struct HealthStatus {
113 pub status: String,
114 pub timestamp: String,
115 pub uptime_seconds: u64,
116 pub version: String,
117 }
118
119 impl HealthStatus {
120 pub fn healthy(uptime_seconds: u64, version: &str) -> Self {
121 Self {
122 status: "healthy".to_string(),
123 timestamp: chrono::Utc::now().to_rfc3339(),
124 uptime_seconds,
125 version: version.to_string(),
126 }
127 }
128
129 pub fn unhealthy(reason: &str, uptime_seconds: u64, version: &str) -> Self {
130 Self {
131 status: format!("unhealthy: {}", reason),
132 timestamp: chrono::Utc::now().to_rfc3339(),
133 uptime_seconds,
134 version: version.to_string(),
135 }
136 }
137 }
138}
139
140pub mod errors {
142 use axum::{http::StatusCode, Json};
143 use serde_json::json;
144
145 pub fn json_error(status: StatusCode, message: &str) -> (StatusCode, Json<serde_json::Value>) {
147 let error_response = json!({
148 "error": {
149 "message": message,
150 "status_code": status.as_u16()
151 },
152 "timestamp": chrono::Utc::now().to_rfc3339()
153 });
154
155 (status, Json(error_response))
156 }
157
158 pub fn json_success<T: serde::Serialize>(data: T) -> (StatusCode, Json<serde_json::Value>) {
160 let success_response = json!({
161 "success": true,
162 "data": data,
163 "timestamp": chrono::Utc::now().to_rfc3339()
164 });
165
166 (StatusCode::OK, Json(success_response))
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_create_socket_addr() {
176 let addr = create_socket_addr("127.0.0.1", 9080).unwrap();
177 assert_eq!(addr.to_string(), "127.0.0.1:9080");
178 }
179
180 #[test]
181 fn test_server_config() {
182 let config = ServerConfig::http(3000);
183 assert_eq!(config.port, 3000);
184 assert_eq!(config.host, "0.0.0.0");
185 matches!(config.server_type, ServerType::HTTP);
186 }
187
188 #[test]
189 fn test_localhost_socket_addr() {
190 let addr = localhost_socket_addr(9080);
191 assert_eq!(addr.to_string(), "127.0.0.1:9080");
192 }
193
194 #[test]
195 fn test_wildcard_socket_addr() {
196 let addr = wildcard_socket_addr(9080);
197 assert_eq!(addr.to_string(), "0.0.0.0:9080");
198 }
199}