yahf 0.0.2

Yet Another HTTP Framework focused on DX
Documentation
use std::{collections::HashMap, sync::Arc};

use futures::Future;

use crate::{
    handler::{encapsulate_runner, BoxedHandler, RefHandler},
    middleware::{AfterMiddleware, MiddlewareFactory, PreMiddleware},
    request::Request,
    response::Response,
    result::InternalResult,
};

#[derive(Default)]
struct Node<'a> {
    childrens: Option<HashMap<&'a str, Node<'a>>>,
    wildcard_node: Option<Box<Node<'a>>>,
    value: Option<BoxedHandler>,
}

#[derive(Default)]
pub struct RouterTree<'a> {
    root: Node<'a>,
}

fn is_parameter_declaration(value: &str) -> bool {
    value.starts_with('{') && value.ends_with('}')
}

impl<'a> RouterTree<'a> {
    pub fn new() -> Self {
        Self {
            root: Node::default(),
        }
    }

    pub fn apply<PreM, FutP, ResultP, AfterM, FutA, ResultA>(
        mut self,
        middleware_factory: Arc<MiddlewareFactory<PreM, AfterM>>,
    ) -> Self
    where
        PreM: PreMiddleware<FutCallResponse = FutP> + 'static,
        AfterM: AfterMiddleware<FutCallResponse = FutA> + 'static,
        FutP: Future<Output = ResultP> + Send + 'static,
        FutA: Future<Output = ResultA> + Send + 'static,
        ResultP: Into<InternalResult<Request<String>>> + Send + 'static,
        ResultA: Into<InternalResult<Response<String>>> + Send + 'static,
    {
        Self::rec_apply(&mut self.root, middleware_factory);
        self
    }

    fn rec_apply<PreM, FutP, ResultP, AfterM, FutA, ResultA>(
        actual_node: &mut Node<'a>,
        middleware_factory: Arc<MiddlewareFactory<PreM, AfterM>>,
    ) where
        PreM: PreMiddleware<FutCallResponse = FutP> + 'static,
        AfterM: AfterMiddleware<FutCallResponse = FutA> + 'static,
        FutP: Future<Output = ResultP> + Send + 'static,
        FutA: Future<Output = ResultA> + Send + 'static,
        ResultP: Into<InternalResult<Request<String>>> + Send + 'static,
        ResultA: Into<InternalResult<Response<String>>> + Send + 'static,
    {
        actual_node.apply_middlewares(middleware_factory.clone());

        match (
            actual_node.childrens.as_mut(),
            actual_node
                .wildcard_node
                .as_mut(),
        ) {
            (None, None) => {}
            (None, Some(wildcard)) => {
                Self::rec_apply(wildcard, middleware_factory);
            }
            (Some(childrens), None) => {
                childrens
                    .iter_mut()
                    .for_each(|(_, node)| {
                        Self::rec_apply(node, middleware_factory.clone());
                    });
            }
            (Some(childrens), Some(wildcard)) => {
                Self::rec_apply(wildcard, middleware_factory.clone());

                childrens
                    .iter_mut()
                    .for_each(|(_, node)| {
                        Self::rec_apply(node, middleware_factory.clone());
                    });
            }
        };
    }

    pub fn extend(&mut self, another_handler: RouterTree<'a>) {
        let root = another_handler.root;

        self.rec_extend(root, "".to_owned());
    }

    fn rec_extend(&mut self, node: Node<'a>, mut path: String) {
        if node.value.is_some() {
            return self.insert(Box::leak(path.into_boxed_str()), node.value.unwrap());
        }

        match (node.childrens, node.wildcard_node) {
            (None, None) => {}
            (None, Some(wildcard_node)) => {
                path.push_str("/{wildcard_node}");
                self.rec_extend(*wildcard_node, path);
            }
            (Some(childrens), None) => {
                childrens
                    .into_iter()
                    .for_each(|(next_path_segment, node)| {
                        self.rec_extend(node, format!("{}/{}", path, next_path_segment));
                    });
            }
            (Some(childrens), Some(wildcard_node)) => {
                childrens
                    .into_iter()
                    .for_each(|(next_path_segment, node)| {
                        self.rec_extend(node, format!("{}/{}", path, next_path_segment));
                    });

                path.push_str("{wildcard_node}");
                self.rec_extend(*wildcard_node, path);
            }
        };
    }

    pub fn insert(&mut self, path: &'a str, handler: BoxedHandler) {
        let mut node = &mut self.root;
        for splitted_path in path
            .split('/')
            .filter(|x| !x.is_empty())
        {
            if is_parameter_declaration(splitted_path) {
                node = node.add_wildcard_node();
                continue;
            }

            node = node.add_normal_node(splitted_path);
        }

        if node.value.is_some() {
            panic!("{} already defined", path);
        }
        node.value = Some(handler);
    }

    pub fn get(&self, path: &str) -> Option<RefHandler<'_>> {
        let mut root = &self.root;

        for splitted_path in path
            .split('/')
            .filter(|x| !x.is_empty())
        {
            match (&root.childrens, &root.wildcard_node) {
                (None, None) => return None,
                (None, Some(wildcard_node)) => root = wildcard_node.as_ref(),
                (Some(childrens), None) => {
                    if childrens.contains_key(splitted_path) {
                        root = childrens
                            .get(splitted_path)
                            .unwrap();
                    } else {
                        return None;
                    }
                }
                (Some(childrens), Some(wildcard_node)) => {
                    if childrens.contains_key(splitted_path) {
                        root = childrens
                            .get(splitted_path)
                            .unwrap();
                        continue;
                    }

                    root = wildcard_node.as_ref();
                }
            }
        }

        root.value
            .as_ref()
            .map(|boxed| boxed.as_ref())
    }
}

impl<'a> Node<'a> {
    fn add_wildcard_node(&mut self) -> &mut Self {
        if self.wildcard_node.is_some() {
            return self
                .wildcard_node
                .as_mut()
                .unwrap()
                .as_mut();
        }

        self.wildcard_node = Some(Box::default());
        self.wildcard_node
            .as_mut()
            .unwrap()
            .as_mut()
    }

    fn apply_middlewares<PreM, FutP, ResultP, AfterM, FutA, ResultA>(
        &mut self,
        middleware_factory: Arc<MiddlewareFactory<PreM, AfterM>>,
    ) where
        PreM: PreMiddleware<FutCallResponse = FutP> + 'static,
        AfterM: AfterMiddleware<FutCallResponse = FutA> + 'static,
        FutP: Future<Output = ResultP> + Send + 'static,
        FutA: Future<Output = ResultA> + Send + 'static,
        ResultP: Into<InternalResult<Request<String>>> + Send + 'static,
        ResultA: Into<InternalResult<Response<String>>> + Send + 'static,
    {
        if let Some(value) = self.value.as_mut() {
            let built = middleware_factory.build(
                value.clone(),
                &String::with_capacity(0),
                &String::with_capacity(0),
            );

            self.value = Some(Box::new(encapsulate_runner(
                built,
                &String::with_capacity(0),
                &String::with_capacity(0),
            )));
        }
    }

    fn add_normal_node(&mut self, path: &'a str) -> &mut Self {
        if self.childrens.is_none() {
            self.childrens = Some(HashMap::new());
        }

        match self.childrens.as_mut() {
            Some(childrens) => childrens
                .entry(path)
                .or_insert(Node::default()),
            None => {
                unreachable!("LALALALALA")
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::handler::encapsulate_runner;

    use super::RouterTree;

    async fn handler_example() -> String {
        "Example".into()
    }

    #[test]
    fn test_add_normal_route() {
        let mut tree = RouterTree::new();
        tree.insert(
            "/",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        assert!(tree.get("/").is_some())
    }

    #[test]
    fn test_add_wildcard_route() {
        let mut tree = RouterTree::new();
        tree.insert(
            "/something/{wildcard}/something",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        assert!(tree
            .get("/something/{wildcard}/something")
            .is_some());
    }

    #[test]
    fn test_mixed_router() {
        let mut tree = RouterTree::new();

        tree.insert(
            "/",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        tree.insert(
            "/something/{wildcard}/something",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        tree.insert(
            "/something/newsomething/newsomething",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        assert!(tree
            .get("/something/{wildcard}/something")
            .is_some());
        assert!(tree.get("/").is_some());
        assert!(tree
            .get("/something/newsomething/newsomething")
            .is_some());
    }

    #[test]
    fn test_get_on_wrong_path() {
        let mut tree = RouterTree::new();

        tree.insert(
            "/",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        tree.insert(
            "/something/{wildcard}/something",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        tree.insert(
            "/something/newsomething/newsomething",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        assert!(tree
            .get("/{wildcard}/something")
            .is_none());
        assert!(tree.get("/nothing").is_none());
        assert!(tree
            .get("/something/newsomething/")
            .is_none());
    }

    #[test]
    fn test_extend_another_router_tree() {
        let mut tree = RouterTree::new();

        tree.insert(
            "/",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        let mut another_tree = RouterTree::new();

        another_tree.insert(
            "/something",
            Box::new(encapsulate_runner(
                handler_example,
                &(),
                &String::with_capacity(0),
            )),
        );

        tree.extend(another_tree);

        assert!(tree.get("/").is_some());
        assert!(tree
            .get("/something")
            .is_some());
    }
}