use axum::Router;
use std::sync::Arc;
use tokio::net::TcpListener;
pub trait ExtensionTrait: Send + Sync {
fn name(&self) -> &'static str;
fn extend(&self, router: Router) -> Router;
}
pub struct CoreX {
router: Router,
extensions: Vec<Arc<dyn ExtensionTrait>>,
host: String,
port: u16,
}
impl CoreX {
pub fn new(host: String, port: u16) -> Self {
Self {
router: Router::new(),
extensions: Vec::new(),
host,
port,
}
}
pub fn register_extension(&mut self, extension: Arc<dyn ExtensionTrait>) {
self.extensions.push(extension);
}
pub fn build(self) -> Router {
let mut router = self.router;
for extension in self.extensions {
router = extension.extend(router);
}
router
}
pub async fn run(self) {
let addr = format!("{}:{}", self.host, self.port);
let router = self.build();
println!("Server running at http://{}", addr);
let listener = TcpListener::bind(&addr).await.unwrap();
axum::serve(listener, router).await.unwrap();
}
}
#[cfg(test)]
mod tests {
use super::*;
use axum::{routing::get, Json};
use serde_json::json;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
struct TestExtension;
impl ExtensionTrait for TestExtension {
fn name(&self) -> &'static str {
"TestExtension"
}
fn extend(&self, router: Router) -> Router {
router.route(
"/test",
get(|| async { Json(json!({ "message": "Test endpoint" })) }),
)
}
}
#[tokio::test]
async fn test_core_with_extension() {
let mut core = CoreX::new("127.0.0.1".to_string(), 3000);
core.register_extension(Arc::new(TestExtension));
let handle = tokio::spawn(async move {
core.run().await;
});
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap();
let request = "GET /test HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
stream.write_all(request.as_bytes()).await.unwrap();
let mut buffer = [0; 1024];
let n = stream.read(&mut buffer).await.unwrap();
let response = String::from_utf8_lossy(&buffer[..n]);
assert!(response.contains("Test endpoint"));
handle.abort();
}
#[tokio::test]
async fn test_core_without_extensions() {
let core = CoreX::new("127.0.0.1".to_string(), 3001);
let handle = tokio::spawn(async move {
core.run().await;
});
tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
let mut stream = TcpStream::connect("127.0.0.1:3001").await.unwrap();
let request = "GET / HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n";
stream.write_all(request.as_bytes()).await.unwrap();
let mut buffer = [0; 1024];
let n = stream.read(&mut buffer).await.unwrap();
let response = String::from_utf8_lossy(&buffer[..n]);
assert!(response.contains("404 Not Found"));
handle.abort();
}
}