1use axum::Router;
2use std::sync::Arc;
3use tokio::net::TcpListener;
4
5pub trait ExtensionTrait: Send + Sync {
8 fn name(&self) -> &'static str;
10
11 fn extend(&self, router: Router) -> Router;
13}
14
15pub struct CoreX {
18 router: Router,
19 extensions: Vec<Arc<dyn ExtensionTrait>>,
20 host: String,
21 port: u16,
22}
23
24impl CoreX {
25 pub fn new(host: String, port: u16) -> Self {
31 Self {
32 router: Router::new(),
33 extensions: Vec::new(),
34 host,
35 port,
36 }
37 }
38
39 pub fn register_extension(&mut self, extension: Arc<dyn ExtensionTrait>) {
44 self.extensions.push(extension);
45 }
46
47 pub fn build(self) -> Router {
49 let mut router = self.router;
50 for extension in self.extensions {
51 router = extension.extend(router);
52 }
53 router
54 }
55
56 pub async fn run(self) {
58 let addr = format!("{}:{}", self.host, self.port);
59 let router = self.build();
60 println!("Server running at http://{}", addr);
61
62 let listener = TcpListener::bind(&addr).await.unwrap();
63 axum::serve(listener, router).await.unwrap();
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use axum::{routing::get, Json};
71 use serde_json::json;
72 use tokio::io::{AsyncReadExt, AsyncWriteExt};
73 use tokio::net::TcpStream;
74
75 struct TestExtension;
77
78 impl ExtensionTrait for TestExtension {
79 fn name(&self) -> &'static str {
80 "TestExtension"
81 }
82
83 fn extend(&self, router: Router) -> Router {
84 router.route(
85 "/test",
86 get(|| async { Json(json!({ "message": "Test endpoint" })) }),
87 )
88 }
89 }
90
91 #[tokio::test]
93 async fn test_core_with_extension() {
94 let mut core = CoreX::new("127.0.0.1".to_string(), 3000);
95 core.register_extension(Arc::new(TestExtension));
96
97 let handle = tokio::spawn(async move {
99 core.run().await;
100 });
101
102 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
104
105 let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap();
107 let request = "GET /test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
108 stream.write_all(request.as_bytes()).await.unwrap();
109
110 let mut buffer = [0; 1024];
111 let n = stream.read(&mut buffer).await.unwrap();
112 let response = String::from_utf8_lossy(&buffer[..n]);
113
114 assert!(response.contains("Test endpoint"));
115
116 handle.abort();
118 }
119
120 #[tokio::test]
122 async fn test_core_without_extensions() {
123 let core = CoreX::new("127.0.0.1".to_string(), 3001);
124
125 let handle = tokio::spawn(async move {
127 core.run().await;
128 });
129
130 tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
132
133 let mut stream = TcpStream::connect("127.0.0.1:3001").await.unwrap();
135 let request = "GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
136 stream.write_all(request.as_bytes()).await.unwrap();
137
138 let mut buffer = [0; 1024];
139 let n = stream.read(&mut buffer).await.unwrap();
140 let response = String::from_utf8_lossy(&buffer[..n]);
141
142 assert!(response.contains("404 Not Found"));
143
144 handle.abort();
146 }
147}