1use axum::{Router, routing::get};
13use std::net::SocketAddr;
14use tokio::net::TcpListener;
15
16#[derive(thiserror::Error, Debug)]
18pub enum RextCoreError {
19 #[error("Failed to bind to address {address}: {source}")]
20 BindError {
21 address: SocketAddr,
22 source: std::io::Error,
23 },
24 #[error("Server failed to start: {0}")]
25 ServerStart(#[from] std::io::Error),
26}
27
28pub struct ServerConfig {
30 pub host: [u8; 4],
31 pub port: u16,
32}
33
34impl Default for ServerConfig {
35 fn default() -> Self {
36 Self {
37 host: [0, 0, 0, 0],
38 port: 3000,
39 }
40 }
41}
42
43pub fn create_router() -> Router {
45 Router::new().route("/", get(root))
46}
47
48pub async fn server_blocking(config: ServerConfig) -> Result<(), RextCoreError> {
51 let app = create_router();
52 let address = SocketAddr::from((config.host, config.port));
53
54 let listener = TcpListener::bind(address)
55 .await
56 .map_err(|e| RextCoreError::BindError { address, source: e })?;
57
58 axum::serve(listener, app).await?;
59 Ok(())
60}
61
62pub async fn server_non_blocking(
65 config: ServerConfig,
66) -> Result<
67 (
68 SocketAddr,
69 tokio::task::JoinHandle<Result<(), RextCoreError>>,
70 ),
71 RextCoreError,
72> {
73 let app = create_router();
74 let address = SocketAddr::from((config.host, config.port));
75
76 let listener = TcpListener::bind(address)
77 .await
78 .map_err(|e| RextCoreError::BindError { address, source: e })?;
79
80 let bound_addr = listener.local_addr().map_err(RextCoreError::ServerStart)?;
82
83 let server_handle = tokio::spawn(async move {
85 axum::serve(listener, app)
86 .await
87 .map_err(RextCoreError::ServerStart)
88 });
89
90 Ok((bound_addr, server_handle))
91}
92
93async fn root() -> &'static str {
94 "Hello, World!"
95}