dce-router 1.0.0

A router for all type programming api route.
Documentation
use std::any::{Any, type_name};
use std::collections::HashMap;
use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::sync::Arc;
use crate::api::{Api, ApiTrait};
use crate::serializer::{Deserializer, Serializable, Serialized};
use crate::router::Router;
use crate::protocol::RoutableProtocol;
use dce_util::mixed::{DceErr, DceResult};
use serde::Serialize;


#[derive(Debug)]
pub struct Context<Rp: RoutableProtocol + 'static> {
    router: Arc<Router<Rp>>,
    api: Option<&'static (dyn ApiTrait<Rp> + Send + Sync)>,
    rp: Option<Rp>,
    path_params: HashMap<&'static str, PathParam>,
    suffix: Option<&'static str>,
    data: HashMap<String, Box<dyn Any + Send>>,
}

impl<Rp: RoutableProtocol + Debug + 'static> Context<Rp> {
    pub fn new(router: Arc<Router<Rp>>, rp: Rp, data: HashMap<String, Box<dyn Any + Send>>) -> Context<Rp> {
        Context { router, api: None, rp: Some(rp), path_params: Default::default(), suffix: None, data, }
    }

    pub fn router(&self) -> &Arc<Router<Rp>> {
        &self.router
    }

    pub fn api(&self) -> Option<&'static (dyn ApiTrait<Rp> + Send + Sync)> {
        self.api
    }

    pub fn rp(&self) -> &Rp {
        self.rp.as_ref().expect("Routable protocol data has been taken, should not borrow it anymore")
    }

    pub fn rp_mut(&mut self) -> &mut Rp {
        self.rp.as_mut().expect("Routable protocol data has been taken, should not borrow mut anymore")
    }
    
    pub fn take_rp(&mut self) -> Option<Rp> {
        self.rp.take()
    }

    pub fn params(&self) -> &HashMap<&'static str, PathParam> {
        &self.path_params
    }

    pub fn param(&self, key: &str) -> DceResult<&PathParam> {
        self.path_params.get(key).ok_or(DceErr::openly0(format!("no param passed with name '{}'", key)))
    }

    pub fn suffix(&mut self) -> &'static str {
        match self.suffix {
            Some(suffix) => suffix,
            None => {
                self.suffix = self.api.iter().find_map(|a| a.suffixes().iter().map(|suffix| &**suffix)

                    .find(|s| self.rp().path().ends_with(format!("{}{}", self.router.suffix_boundary(), s).as_str()))).or_else(|| Some(""));
                self.suffix()
            },
        }
    }

    pub fn data(&self) -> &HashMap<String, Box<dyn Any + Send>> {
        &self.data
    }

    pub fn put_data(&mut self, key: String, value: Box<dyn Any + Send>) {
        self.data.insert(key, value);
    }

    pub fn get_as<S: 'static>(&self, key: &str) -> DceResult<&S> {
        let type_name = type_name::<S>();
        self.data.get(key).ok_or_else(|| DceErr::closed0(format!("{} has not bound yet", type_name)))?
            .downcast_ref().ok_or_else(|| DceErr::closed0(format!("Box cannot downcast to {} ref", type_name)))
    }

    pub fn get_as_mut<S: 'static>(&mut self, key: &str) -> DceResult<&mut S> {
        let type_name = type_name::<S>();
        self.data.get_mut(key).ok_or_else(|| DceErr::closed0(format!("{} has not bound yet", type_name)))?
            .downcast_mut().ok_or_else(|| DceErr::closed0(format!("Box cannot downcast to {} ref", type_name)))
    }

    pub fn set_routed_info(&mut self, api: &'static (dyn ApiTrait<Rp> + Send + Sync), params: HashMap<&'static str, PathParam>, suffix: Option<&'static str>) {
        self.api = Some(api);
        self.path_params = params;
        self.suffix = suffix;
    }
}

#[derive(Debug)]
pub enum PathParam {
    Option(Option<String>),
    Required(String),
    Vector(Vec<String>),
}

impl PathParam {
    pub fn as_str(&self) -> Option<&str> {
        match self {
            PathParam::Option(Some(param)) => Some(param.as_str()),
            PathParam::Option(_) => None,
            PathParam::Required(param) => Some(param.as_str()),
            PathParam::Vector(_) => panic!("Vec param cannot get as str"),
        }
    }

    pub fn as_vec(&self) -> &Vec<String> {
        match self {
            PathParam::Vector(param) => param,
            _ => panic!("Non vec type param cannot get as vec"),
        }
    }
}



#[derive(Debug)]
pub struct Request<'a, Rp, ReqDto, RespDto>
where Rp: RoutableProtocol + Send + Sync + Debug + 'static,
      ReqDto: 'static,
      RespDto: 'static
{
    api: &'static Api<Rp, ReqDto, RespDto>,
    context: &'a mut Context<Rp>,
}

impl<'a, Rp, ReqDto, RespDto> Request<'a, Rp, ReqDto, RespDto>
where Rp: RoutableProtocol + Send + Sync + Debug + 'static,
{
    #[cfg(feature = "async")]
    pub async fn req<Req: From<ReqDto>>(&mut self) -> DceResult<Req>  {
        let body = self.context.rp_mut().body().await?;
        self.parse(body, self.api.deserializers()).map(Req::from)
    }

    #[cfg(not(feature = "async"))]
    pub fn req<Req: From<ReqDto>>(&mut self) -> DceResult<Req> {
        let body = self.context.rp_mut().body()?;
        self.parse(body, self.api.deserializers()).map(Req::from)
    }

    #[cfg(feature = "async")]
    pub async fn dto(&mut self) -> DceResult<ReqDto>  {
        let body = self.context.rp_mut().body().await?;
        self.parse(body, self.api.deserializers())
    }

    #[cfg(not(feature = "async"))]
    pub fn dto(&mut self) -> DceResult<ReqDto> {
        let body = self.context.rp_mut().body()?;
        self.parse(body, self.api.deserializers())
    }

    fn parse(&self, serialized: Serialized, deserializers: &[Box<dyn Deserializer<ReqDto> + Send + Sync>]) -> DceResult<ReqDto> {
        Rp::deserialize(deserializers, serialized, &self.context)
    }

    pub fn status<Resp: Into<RespDto>>(self, status: bool, data: Option<Resp>, message: Option<String>, code: isize) -> DceResult<Option<Response<Rp::Resp>>> {
        let Self{context, api} = self;
        Rp::pack_responsible::<RespDto>(context, api.serializers(), Serializable::Status(ResponseStatus {
            status,
            code,
            message: message.unwrap_or("".to_string()),
            data: data.map(|resp| resp.into()),
        }))
    }

    pub fn success(self, data: Option<RespDto>) -> DceResult<Option<Response<Rp::Resp>>> {
        self.status(true, data, None, 0)
    }

    pub fn fail(self, message: Option<String>, code: isize) -> DceResult<Option<Response<Rp::Resp>>> {
        self.status::<RespDto>(false, None, message, code)
    }

    pub fn resp<Resp: Into<RespDto>>(self, resp: Resp) -> DceResult<Option<Response<Rp::Resp>>> {
        let Self{context, api} = self;
        Rp::pack_responsible(context, api.serializers(), Serializable::Dto(resp.into()))
    }

    pub fn end(self, resp: Option<RespDto>) -> DceResult<Option<Response<Rp::Resp>>> {
        if let Some(resp) = resp {
            let Self{context , api} = self;
            Rp::pack_responsible::<RespDto>(context, api.serializers(), Serializable::Dto(resp.into()))
        } else {
            Ok(None)
        }
    }

    pub fn pack(self, serialized: Serialized) -> DceResult<Option<Response<Rp::Resp>>> {
        Ok(Some(Response::Serialized(serialized)))
    }

    pub fn raw_resp(self, resp: Rp::Resp) -> DceResult<Option<Response<Rp::Resp>>> {
        Ok(Some(Response::Raw(resp)))
    }
    
    pub fn new(api: &'static Api<Rp, ReqDto, RespDto>, context: &'a mut Context<Rp>) -> Request<'a, Rp, ReqDto, RespDto> {
        Request { api, context }
    }
}

impl<Rp, ReqDto, RespDto> Deref for Request<'_, Rp, ReqDto, RespDto>
    where Rp: RoutableProtocol + Send + Sync + Debug + 'static,  {
    type Target = Context<Rp>;

    fn deref(&self) -> &Self::Target {
        &self.context
    }
}

impl<Rp, ReqDto, RespDto> DerefMut for Request<'_, Rp, ReqDto, RespDto>
    where Rp: RoutableProtocol + Send + Sync + Debug + 'static, {

    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.context
    }
}

pub trait RequestTrait {
    type Rp: RoutableProtocol;
    type ReqDto;
    type RespDto;
}

impl<Rp, ReqDto, RespDto> RequestTrait for Request<'_, Rp, ReqDto, RespDto>
    where Rp: RoutableProtocol + Send + Sync + Debug + 'static, {
    type Rp = Rp;
    type ReqDto = ReqDto;
    type RespDto = RespDto;
}


#[derive(Debug)]
pub enum Response<Resp> {
    Serialized(Serialized),
    Raw(Resp),
}


#[derive(Debug, Serialize)]
pub struct ResponseStatus<Dto> {
    pub status: bool,
    pub code: isize,
    pub message: String,
    pub data: Option<Dto>,
}