sark 0.1.0

Simple Asynchronous Rust webKit
Documentation
use crate::{
    http::{Request, Response},
    error::{Result, Error},
    service::Service,
};
use http::Method;
use std::collections::HashMap;
use std::marker::PhantomData;

pub struct Router<S, N = Nil> {
    node: N,
    _marker: PhantomData<S>,
}

pub struct Nil;

impl<S> Router<S, Nil> {
    pub fn new() -> Self {
        Self {
            node: Nil,
            _marker: PhantomData,
        }
    }
}

impl<S, N> Router<S, N> {
    pub fn route<H>(self, method: Method, path: &str, handler: H) -> Router<S, Node<H, N>>
    where
        H: Service<S>,
    {
        Router {
            node: Node {
                method,
                path: path.to_string(),
                handler,
                next: self.node,
            },
            _marker: PhantomData,
        }
    }
}

impl<S, N> Service<S> for Router<S, N> 
where 
    N: RouteNode<S>,
{
    async fn call(&self, req: Request, state: &S) -> Result<Response> {
        self.node.match_route(req, state).await
    }
}

pub trait RouteNode<S> {
    async fn match_route(&self, req: Request, state: &S) -> Result<Response>;
}

pub struct Node<H, N> {
    method: Method,
    path: String,
    handler: H,
    next: N,
}

impl<S> RouteNode<S> for Nil {
    async fn match_route(&self, _req: Request, _state: &S) -> Result<Response> {
        Err(Error::NotFound)
    }
}

impl<S, H, N> RouteNode<S> for Node<H, N>
where
    H: Service<S>,
    N: RouteNode<S>,
{
    async fn match_route(&self, req: Request, state: &S) -> Result<Response> {
        let method = req.method();
        let path = req.uri().path().to_string();
        
        if *method == self.method {
            if !self.path.contains(':') {
                if self.path == path {
                    return self.handler.call(req, state).await;
                }
            } else {
                let mut req_clone = req.clone();
                if extract_path_match(&self.path, &path, &mut req_clone) {
                    return self.handler.call(req_clone, state).await;
                }
            }
        }
        
        self.next.match_route(req, state).await
    }
}

fn extract_path_match(route_path: &str, req_path: &str, req: &mut Request) -> bool {
    let route_segments: Vec<&str> = route_path.split('/').filter(|s| !s.is_empty()).collect();
    let req_segments: Vec<&str> = req_path.split('/').filter(|s| !s.is_empty()).collect();
    
    if route_segments.len() != req_segments.len() {
        return false;
    }
    
    let mut params = HashMap::new();
    for (i, segment) in route_segments.iter().enumerate() {
        if segment.starts_with(':') {
            let param_name = &segment[1..];
            params.insert(param_name.to_string(), req_segments[i].to_string());
        } else if *segment != req_segments[i] {
            return false;
        }
    }
    
    if !params.is_empty() {
        req.set_path_params(params);
    }
    
    true
}