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)
}
}