use crate::error::Result;
use crate::middleware::{MiddlewareStack, Next};
use crate::request::Request;
use crate::response::Response;
use crate::router::Router;
use bytes::Bytes;
use http_body_util::Full;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{body::Incoming, Method};
use hyper_util::rt::TokioIo;
use std::convert::Infallible;
use std::future::Future;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use tokio::net::TcpListener;
use tracing::{error, info};
pub type HandlerFn =
Arc<dyn Fn(Request, Response) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync>;
pub struct RustyX {
router: Arc<std::sync::RwLock<Router>>,
middleware_stack: Arc<std::sync::RwLock<MiddlewareStack>>,
settings: Arc<std::sync::RwLock<AppSettings>>,
}
#[derive(Debug, Clone)]
pub struct AppSettings {
pub json_spaces: Option<usize>,
pub trust_proxy: bool,
pub case_sensitive_routing: bool,
pub strict_routing: bool,
pub env: String,
}
impl Default for AppSettings {
fn default() -> Self {
Self {
json_spaces: None,
trust_proxy: false,
case_sensitive_routing: false,
strict_routing: false,
env: std::env::var("RUST_ENV").unwrap_or_else(|_| "development".to_string()),
}
}
}
impl RustyX {
pub fn new() -> Self {
Self {
router: Arc::new(std::sync::RwLock::new(Router::new())),
middleware_stack: Arc::new(std::sync::RwLock::new(MiddlewareStack::new())),
settings: Arc::new(std::sync::RwLock::new(AppSettings::default())),
}
}
pub fn set<T: ToString>(&self, key: &str, value: T) -> &Self {
if let Ok(mut settings) = self.settings.write() {
match key {
"json_spaces" => settings.json_spaces = value.to_string().parse().ok(),
"trust_proxy" => settings.trust_proxy = value.to_string().parse().unwrap_or(false),
"case_sensitive_routing" => {
settings.case_sensitive_routing = value.to_string().parse().unwrap_or(false)
}
"strict_routing" => {
settings.strict_routing = value.to_string().parse().unwrap_or(false)
}
"env" => settings.env = value.to_string(),
_ => {}
}
}
self
}
pub fn use_middleware<F, Fut>(&self, middleware: F) -> &Self
where
F: Fn(Request, Response, Next) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
if let Ok(mut stack) = self.middleware_stack.write() {
stack.push(Box::new(move |req, res, next| {
Box::pin(middleware(req, res, next))
}));
}
self
}
pub fn use_router(&self, path: &str, router: Router) -> &Self {
if let Ok(mut main_router) = self.router.write() {
main_router.mount(path, router);
}
self
}
pub fn get<F, Fut>(&self, path: &str, handler: F) -> &Self
where
F: Fn(Request, Response) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.route(Method::GET, path, handler)
}
pub fn post<F, Fut>(&self, path: &str, handler: F) -> &Self
where
F: Fn(Request, Response) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.route(Method::POST, path, handler)
}
pub fn put<F, Fut>(&self, path: &str, handler: F) -> &Self
where
F: Fn(Request, Response) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.route(Method::PUT, path, handler)
}
pub fn delete<F, Fut>(&self, path: &str, handler: F) -> &Self
where
F: Fn(Request, Response) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.route(Method::DELETE, path, handler)
}
pub fn patch<F, Fut>(&self, path: &str, handler: F) -> &Self
where
F: Fn(Request, Response) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
self.route(Method::PATCH, path, handler)
}
fn route<F, Fut>(&self, method: Method, path: &str, handler: F) -> &Self
where
F: Fn(Request, Response) -> Fut + Send + Sync + 'static,
Fut: Future<Output = Response> + Send + 'static,
{
if let Ok(mut router) = self.router.write() {
router.add_route(
method,
path,
Arc::new(move |req, res| Box::pin(handler(req, res))),
);
}
self
}
pub async fn listen(self, port: u16) -> Result<()> {
self.listen_with_callback(port, || {
info!("🚀 RustyX server running at http://localhost:{}", port);
})
.await
}
pub async fn listen_with_callback<F>(self, port: u16, callback: F) -> Result<()>
where
F: FnOnce(),
{
let addr = SocketAddr::from(([0, 0, 0, 0], port));
let listener = TcpListener::bind(addr).await?;
callback();
let app = Arc::new(self);
loop {
let (stream, remote_addr) = listener.accept().await?;
let io = TokioIo::new(stream);
let app = Arc::clone(&app);
tokio::spawn(async move {
let service = service_fn(move |req: hyper::Request<Incoming>| {
let app = Arc::clone(&app);
async move {
let response = app.handle_request(req, remote_addr).await;
Ok::<_, Infallible>(response)
}
});
if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
error!("Error serving connection: {:?}", err);
}
});
}
}
async fn handle_request(
&self,
req: hyper::Request<Incoming>,
remote_addr: SocketAddr,
) -> hyper::Response<Full<Bytes>> {
let request = match Request::from_hyper(req, remote_addr).await {
Ok(r) => r,
Err(e) => {
error!("Failed to parse request: {:?}", e);
return Response::new()
.status(400)
.json(serde_json::json!({ "error": "Bad Request" }))
.into_hyper();
}
};
let response = Response::new();
let handler_and_params = {
let router = self.router.read().unwrap();
router
.find_route(request.method(), request.path())
.map(|(h, p)| (Arc::clone(h), p))
};
if let Some((handler, params)) = handler_and_params {
let mut request = request;
request.set_params(params);
let response = handler(request, response).await;
response.into_hyper()
} else {
Response::new()
.status(404)
.json(serde_json::json!({ "error": "Not Found" }))
.into_hyper()
}
}
}
impl Default for RustyX {
fn default() -> Self {
Self::new()
}
}
impl Clone for RustyX {
fn clone(&self) -> Self {
Self {
router: Arc::clone(&self.router),
middleware_stack: Arc::clone(&self.middleware_stack),
settings: Arc::clone(&self.settings),
}
}
}