1use std::net::SocketAddr;
4
5pub fn create_socket_addr(host: &str, port: u16) -> Result<SocketAddr, String> {
15 format!("{}:{}", host, port)
16 .parse()
17 .map_err(|e| format!("Invalid socket address {}:{}: {}", host, port, e))
18}
19
20pub fn localhost_socket_addr(port: u16) -> SocketAddr {
25 SocketAddr::from(([127, 0, 0, 1], port))
26}
27
28pub fn wildcard_socket_addr(port: u16) -> SocketAddr {
33 SocketAddr::from(([0, 0, 0, 0], port))
34}
35
36#[derive(Debug, Clone)]
38pub struct ServerConfig {
39 pub host: String,
41 pub port: u16,
43 pub server_type: ServerType,
45}
46
47#[derive(Debug, Clone)]
49pub enum ServerType {
50 HTTP,
52 WebSocket,
54 GRPC,
56}
57
58impl ServerConfig {
59 pub fn new(host: String, port: u16, server_type: ServerType) -> Self {
61 Self {
62 host,
63 port,
64 server_type,
65 }
66 }
67
68 pub fn http(port: u16) -> Self {
70 Self::new("0.0.0.0".to_string(), port, ServerType::HTTP)
71 }
72
73 pub fn websocket(port: u16) -> Self {
75 Self::new("0.0.0.0".to_string(), port, ServerType::WebSocket)
76 }
77
78 pub fn grpc(port: u16) -> Self {
80 Self::new("0.0.0.0".to_string(), port, ServerType::GRPC)
81 }
82
83 pub fn socket_addr(&self) -> Result<SocketAddr, String> {
85 create_socket_addr(&self.host, self.port)
86 }
87
88 pub fn description(&self) -> String {
90 match self.server_type {
91 ServerType::HTTP => format!("HTTP server on {}:{}", self.host, self.port),
92 ServerType::WebSocket => format!("WebSocket server on {}:{}", self.host, self.port),
93 ServerType::GRPC => format!("gRPC server on {}:{}", self.host, self.port),
94 }
95 }
96}
97
98pub trait ServerStarter {
103 fn server_type(&self) -> ServerType;
105
106 fn port(&self) -> u16;
108
109 fn start_server(
113 self,
114 ) -> impl std::future::Future<Output = Result<(), Box<dyn std::error::Error + Send + Sync>>> + Send;
115}
116
117pub async fn start_server<S: ServerStarter>(
127 server: S,
128) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
129 let port = server.port();
130 let server_type = server.server_type();
131
132 match server_type {
133 ServerType::HTTP => tracing::info!("HTTP listening on port {}", port),
134 ServerType::WebSocket => tracing::info!("WebSocket listening on port {}", port),
135 ServerType::GRPC => tracing::info!("gRPC listening on port {}", port),
136 }
137
138 server.start_server().await
139}
140
141pub mod health {
143 use serde::{Deserialize, Serialize};
144
145 #[derive(Debug, Serialize, Deserialize)]
147 pub struct HealthStatus {
148 pub status: String,
150 pub timestamp: String,
152 pub uptime_seconds: u64,
154 pub version: String,
156 }
157
158 impl HealthStatus {
159 pub fn healthy(uptime_seconds: u64, version: &str) -> Self {
161 Self {
162 status: "healthy".to_string(),
163 timestamp: chrono::Utc::now().to_rfc3339(),
164 uptime_seconds,
165 version: version.to_string(),
166 }
167 }
168
169 pub fn unhealthy(reason: &str, uptime_seconds: u64, version: &str) -> Self {
171 Self {
172 status: format!("unhealthy: {}", reason),
173 timestamp: chrono::Utc::now().to_rfc3339(),
174 uptime_seconds,
175 version: version.to_string(),
176 }
177 }
178 }
179}
180
181pub mod errors {
183 use axum::{http::StatusCode, Json};
184 use serde_json::json;
185
186 pub fn json_error(status: StatusCode, message: &str) -> (StatusCode, Json<serde_json::Value>) {
195 let error_response = json!({
196 "error": {
197 "message": message,
198 "status_code": status.as_u16()
199 },
200 "timestamp": chrono::Utc::now().to_rfc3339()
201 });
202
203 (status, Json(error_response))
204 }
205
206 pub fn json_success<T: serde::Serialize>(data: T) -> (StatusCode, Json<serde_json::Value>) {
214 let success_response = json!({
215 "success": true,
216 "data": data,
217 "timestamp": chrono::Utc::now().to_rfc3339()
218 });
219
220 (StatusCode::OK, Json(success_response))
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn test_create_socket_addr() {
230 let addr = create_socket_addr("127.0.0.1", 9080).unwrap();
231 assert_eq!(addr.to_string(), "127.0.0.1:9080");
232 }
233
234 #[test]
235 fn test_server_config() {
236 let config = ServerConfig::http(3000);
237 assert_eq!(config.port, 3000);
238 assert_eq!(config.host, "0.0.0.0");
239 matches!(config.server_type, ServerType::HTTP);
240 }
241
242 #[test]
243 fn test_localhost_socket_addr() {
244 let addr = localhost_socket_addr(9080);
245 assert_eq!(addr.to_string(), "127.0.0.1:9080");
246 }
247
248 #[test]
249 fn test_wildcard_socket_addr() {
250 let addr = wildcard_socket_addr(9080);
251 assert_eq!(addr.to_string(), "0.0.0.0:9080");
252 }
253}