dce-router 1.8.0

A router for all type programming api route.
Documentation
use std::{any::Any, collections::HashMap, future::Future, ops::{BitAnd, BitOr}, pin::Pin};

use dce_util::result::{DceResult, DceVoid, OK_VOID};

use crate::{context::{Context, Request}, protocol::RoutableProtocol, router::RouteMatch};

pub const MARK_PATH_PART_SEPARATOR: &str     = "/";
pub const MARK_SUFFIX_SEPARATOR: &str       = "|";
pub const MARK_SUFFIX_BOUNDARY: &str        = ".";
pub const MARK_VARIABLE_OPENER: &str        = "{";
pub const MARK_VARIABLE_CLOSING: &str       = "}";
pub const MARK_VAR_TYPE_OPTIONAL: &str       = "?";
pub const MARK_VAR_TYPE_EMPTABLE_VECTOR: &str = "*";
pub const MARK_VAR_TYPE_VECTOR: &str         = "+";
const EXTRA_SERVE_ADDR_KEY: &str = "$#BIND-HOSTS#";

pub struct Api<Rp: RoutableProtocol + 'static> {
    pub methods: Methods,
    pub path: &'static str,
    pub suffixes: Vec<Suffix>,
    pub id: Option<&'static str>,
    pub omission: bool,
    pub responsive: bool,
    pub redirect: Option<&'static str>,
    pub name: &'static str,
    pub extras: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
    pub handler: Option<Handler<Rp>>,
}

impl<Rp: RoutableProtocol + 'static> Api<Rp> {
    pub fn by_methods(mut self, methods: Methods) -> Self {
        self.methods = methods;
        self
    }

    pub fn by_id(mut self, id: &'static str) -> Self {
        self.id = Some(id);
        self
    }

    pub fn as_omission(mut self) -> Self {
        self.omission = true;
        self
    }

    pub fn as_responsive(mut self) -> Self {
        self.responsive = true;
        self
    }

    pub fn as_unresponsive(mut self) -> Self {
        self.responsive = false;
        self
    }

    pub fn by_redirect(mut self, redirect: &'static str) -> Self {
        self.redirect = Some(redirect);
        self
    }

    pub fn by_name(mut self, name: &'static str) -> Self {
        self.name = name;
        self
    }

    pub fn with(mut self, key: &'static str, val: Box<dyn Any + Send + Sync>) -> Self {
        self.extras.insert(key, val);
        self
    }

    pub fn append(mut self, key: &'static str, mut items: Vec<Box<dyn Any + Send + Sync>>) -> Self {
        if !self.extras.contains_key(key) {
            self.extras.insert(key, Box::new(items));
        } else if let Some(val) = self.extras.get_mut(key) {
            if let Some(exists) = val.downcast_mut::<Vec<Box<dyn Any + Send + Sync>>>() {
                exists.append(&mut items);
            } else {
		        panic!(r#"Api with path "{}" was already has an extra keyd by "{}", but is not a vector value."#, self.path, key);
            }
        }
        self
    }

    pub fn extra_by(&self, key: &str) -> Option<&Box<dyn Any + Send + Sync>> {
        self.extras.get(key)
    }

    pub fn extras_by(&self, key: &str) -> Option<&Vec<Box<dyn Any + Send + Sync>>> {
        self.extras.get(key).map(|v| v.downcast_ref::<Vec<Box<dyn Any + Send + Sync>>>()).flatten()
    }

    pub fn bind_hosts(self, hosts: Vec<String>) -> Self {
        self.append(EXTRA_SERVE_ADDR_KEY, hosts.into_iter().map(|h| Box::new(h) as Box<dyn Any + Send + Sync>).collect::<Vec<_>>())
    }

    pub fn hosts(&self) -> Vec<&str> {
        self.extras_by(EXTRA_SERVE_ADDR_KEY).map_or_else(|| vec![], |vs| vs.iter()

            .filter_map(|v| v.downcast_ref::<String>().map(|s| s.as_str())).collect::<Vec<_>>())
    }

    pub fn bind_handler(mut self, handler: Handler<Rp>) -> Self {
        self.handler = Some(handler);
        self
    }

    pub fn upgrade(mut self) -> Self {
        if self.suffixes.len() > 0 {
            panic!(r#"Please define the suffixes in the end of "Path" but not defined directly"#)
        }
        let last_part_from = self.path.rfind(MARK_PATH_PART_SEPARATOR)
            .map(|i| i + MARK_PATH_PART_SEPARATOR.len()).unwrap_or(0);
        if let Some(bound_index) = self.path[last_part_from..].find(MARK_SUFFIX_BOUNDARY) {
            self.suffixes = self.path[last_part_from + bound_index + MARK_SUFFIX_BOUNDARY.len() ..]
                .split(MARK_SUFFIX_SEPARATOR).map(|s| Suffix(s)).collect();
            self.path = &self.path[..last_part_from+bound_index];
        } else {
            self.suffixes = vec![Suffix("")];
        }
        if self.path.starts_with(MARK_PATH_PART_SEPARATOR) {
		    panic!(r#"Api.Path "{}" cannot be start with "{}""#, MARK_PATH_PART_SEPARATOR, self.path)
        }
        self
    }

    pub fn new(path: &'static str) -> Self {
        Self::new_with(path, Methods(0), vec![], false, None, true, None, "", Default::default(), None)
    }

    pub fn new_with(
        path: &'static str,
        methods: Methods,
        suffixes: Vec<Suffix>,
        omission: bool,
        id: Option<&'static str>,
        responsive: bool,
        redirect: Option<&'static str>,
        name: &'static str,
        extras: HashMap<&'static str, Box<dyn Any + Send + Sync>>,
        handler: Option<Handler<Rp>>,
    ) -> Self {
        Self { path, responsive, methods, suffixes, id, omission, redirect, name, extras, handler, }
    }
}

impl<Rp: RoutableProtocol + 'static> Api<Rp> {
    pub fn sync_handle(&self, ctx: &mut Context<Rp>, routed: RouteMatch<'_, Rp>) -> DceVoid {
        if let Some(Hook::Sync(hook)) = routed.pre_hook {
            hook(ctx)?
        }
        if let Some(Handler::Sync(handler)) = &self.handler {
            handler(Request::new(ctx))?;
        }
        if let Some(Hook::Sync(hook)) = routed.post_hook {
            hook(ctx)?
        }
        OK_VOID
    }

    pub async fn handle(&self, ctx: &mut Context<Rp>, routed: RouteMatch<'_, Rp>) -> DceVoid {
        if let Some(pre_hook) = routed.pre_hook {            
            match pre_hook {
                Hook::Async(hook) => hook(ctx).await?,
                Hook::Sync(hook) => hook(ctx)?,
            };
        }
        if let Some(handler) = &self.handler {
            let req = Request::new(ctx);
            match handler {
                Handler::Async(handler) => handler(req).await?,
                Handler::Sync(handler) => handler(req)?,
            };
        }
        if let Some(post_hook) = routed.post_hook {            
            match post_hook {
                Hook::Async(hook) => hook(ctx).await?,
                Hook::Sync(hook) => hook(ctx)?,
            };
        }
        OK_VOID
    }
}

pub enum Handler<Rp: RoutableProtocol + 'static> {
    Sync(for<'a> fn(Request<'a, Rp>) -> DceResult<()>),
    Async(Box<dyn for<'a> Fn(Request<'a, Rp>) -> Pin<Box<dyn Future<Output = DceResult<()>> + Send + 'a>> + Send + Sync>),
}

pub enum Hook<Rp: RoutableProtocol + 'static> {
    Sync(for<'a> fn(&'a mut Context<Rp>) -> DceResult<()>),
    Async(Box<dyn for<'a> Fn(&'a mut Context<Rp>) -> Pin<Box<dyn Future<Output = DceResult<()>> + Send + 'a>> + Send + Sync>),
}


#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct Suffix(pub &'static str);

impl AsRef<str> for Suffix {
    fn as_ref(&self) -> &str {
        self.0
    }
}

#[derive(Default, Clone, Copy, PartialEq, Eq, Debug)]
pub struct Methods(pub u16);

impl BitOr for Methods {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self::Output {
        Self(self.0 | rhs.0)
    }
}

impl BitAnd for Methods {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self::Output {
        Self(self.0 & rhs.0)
    }
}