rum_framework 0.0.1

A toy web framework inspired by gin-gonic and express-js.
Documentation
use std::collections::BTreeMap;
extern crate mime;
use crate::context::RumContext;
use crate::method::MethodType;

pub struct Router {
    pub route: String,
    children: BTreeMap<String, Router>,
    params_child_route: String,
    controllers: BTreeMap<String, fn(c: &mut RumContext)>,
    full_route: Vec<String>,
    middlewares: Vec<fn(c: &mut RumContext)>,
}

impl Router {
    pub(crate) fn new() -> Router {
        return Router {
            route: String::new(),
            children: BTreeMap::new(),
            params_child_route: String::new(),
            controllers: BTreeMap::new(),
            full_route: Vec::new(),
            middlewares: Vec::new(),
        };
    }

    pub(crate) fn get_info_and_controller(
        &self,
        method_type: MethodType,
        route_segs: &[&str],
    ) -> Option<(&[String], &fn(&mut RumContext))> {
        return match self.search_route(route_segs, 0) {
            Some((full_route, router)) => match router.get_controller(method_type) {
                Some(controller) => Some((full_route, controller)),
                None => None,
            },
            None => None,
        };
    }

    pub(crate) fn exec_middleware(
        &self,
        full_route: &[String],
        cur_index: usize,
        context: &mut RumContext,
    ) {
        for middleware in &self.middlewares {
            middleware(context);
            if context.has_response() {
                return;
            }
        }
        if cur_index + 1 < full_route.len() {
            match self.children.get(&full_route[cur_index + 1]) {
                Some(router) => router.exec_middleware(full_route, cur_index + 1, context),
                None => (),
            }
        }
    }

    pub(crate) fn add_middleware(&mut self, controllers: Vec<fn(&mut RumContext)>) {
        for controller in controllers {
            self.middlewares.push(controller);
        }
    }

    pub(crate) fn get_controller(&self, method_type: MethodType) -> Option<&fn(&mut RumContext)> {
        return self.controllers.get(&method_type.to_string());
    }

    pub(crate) fn search_route(
        &self,
        route_segs: &[&str],
        cur_index: usize,
    ) -> Option<(&[String], &Router)> {
        if cur_index == route_segs.len() - 1 {
            return Some((&self.full_route[..], &self));
        } else {
            return match self.children.get(route_segs[cur_index + 1]) {
                None => {
                    if self.params_child_route != "" {
                        match self.children.get(&self.params_child_route) {
                            None => None,
                            Some(router) => router.search_route(route_segs, cur_index + 1),
                        }
                    } else {
                        None
                    }
                }
                Some(router) => router.search_route(route_segs, cur_index + 1),
            };
        }
    }

    pub(crate) fn search_route_mut(
        &mut self,
        route_segs: &[&str],
        cur_index: usize,
    ) -> Option<&mut Router> {
        if cur_index == route_segs.len() - 1 {
            return Some(self);
        } else {
            return match self.children.get_mut(route_segs[cur_index + 1]) {
                Some(router) => router.search_route_mut(route_segs, cur_index + 1),
                None => None,
            };
        }
    }

    pub(crate) fn show_routes(&self, route: &str) {
        if self.controllers.len() > 0 {
            for controller in &self.controllers {
                println!("|{}| /{}", controller.0, route);
            }
        }
        for value in self.children.values() {
            let new_route = if route == "" {
                format!("{}", value.route)
            } else {
                format!("{}/{}", route, value.route)
            };
            value.show_routes(&new_route);
        }
    }

    pub(crate) fn add_controller(
        &mut self,
        method_type: MethodType,
        controller: fn(&mut RumContext),
    ) {
        let method_type_str = method_type.to_string();
        if self.controllers.contains_key(&method_type_str) {
            panic!(
                "Error: Method->{} Route->{} already exists",
                method_type, self.route
            );
        }
        self.controllers.insert(method_type_str, controller);
    }

    pub(crate) fn modify(&mut self, route_segs: Vec<&str>, cur_index: usize) -> &mut Router {
        if cur_index == route_segs.len() - 1 {
            return self;
        } else {
            let new_seg = route_segs[cur_index + 1];
            if new_seg.starts_with(":") {
                if self.params_child_route != "" && new_seg != self.params_child_route {
                    panic!(
                        "Error: params {} conflict with params {}",
                        new_seg, self.params_child_route
                    );
                }
                self.params_child_route = new_seg.to_string();
            }
            self.children.entry(new_seg.to_string()).or_insert(Router {
                route: new_seg.to_string(),
                children: BTreeMap::new(),
                params_child_route: String::new(),
                controllers: BTreeMap::new(),
                middlewares: Vec::new(),
                full_route: (&route_segs[..=cur_index + 1])
                    .to_vec()
                    .iter()
                    .map(|&s| s.to_string())
                    .collect(),
            });
            let router = self.children.get_mut(route_segs[cur_index + 1]).unwrap();
            return router.modify(route_segs, cur_index + 1);
        }
    }
}