use conjure_error::{Error, InvalidArgument};
use http::{HeaderMap, Method};
use serde::{Deserializer, Serialize};
use std::error;
use std::io::Write;
use crate::{PathParams, QueryParams};
pub type Handler<T, B, R, O> = fn(
service: &T,
path_params: &PathParams,
query_params: &QueryParams,
headers: &HeaderMap,
body: B,
response_visitor: R,
) -> Result<O, Error>;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Parameter {
name: &'static str,
type_: ParameterType,
safe: bool,
}
impl Parameter {
#[inline]
pub const fn new(name: &'static str, type_: ParameterType) -> Parameter {
Parameter {
name,
type_,
safe: false,
}
}
#[inline]
pub const fn with_safe(mut self, safe: bool) -> Parameter {
self.safe = safe;
self
}
#[inline]
pub fn name(&self) -> &'static str {
self.name
}
#[inline]
pub fn type_(&self) -> ParameterType {
self.type_
}
#[inline]
pub fn safe(&self) -> bool {
self.safe
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ParameterType {
Path(PathParameter),
Query(QueryParameter),
Header(HeaderParameter),
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct PathParameter(());
impl PathParameter {
#[inline]
pub const fn new() -> PathParameter {
PathParameter(())
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct QueryParameter {
key: &'static str,
}
impl QueryParameter {
#[inline]
pub const fn new(key: &'static str) -> QueryParameter {
QueryParameter { key }
}
#[inline]
pub fn key(&self) -> &'static str {
self.key
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct HeaderParameter {
header: &'static str,
}
impl HeaderParameter {
#[inline]
pub const fn new(header: &'static str) -> HeaderParameter {
HeaderParameter { header }
}
#[inline]
pub fn header(&self) -> &'static str {
self.header
}
}
pub struct Endpoint<T, B, R>
where
R: VisitResponse,
{
name: &'static str,
method: Method,
path: &'static str,
handler: Handler<T, B, R, R::Output>,
parameters: &'static [Parameter],
}
impl<T, B, R> Endpoint<T, B, R>
where
R: VisitResponse,
{
pub fn new(
name: &'static str,
method: Method,
path: &'static str,
handler: Handler<T, B, R, R::Output>,
parameters: &'static [Parameter],
) -> Endpoint<T, B, R> {
Endpoint {
name,
method,
path,
handler,
parameters,
}
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn method(&self) -> &Method {
&self.method
}
pub fn path(&self) -> &'static str {
self.path
}
pub fn handler(&self) -> Handler<T, B, R, R::Output> {
self.handler
}
pub fn parameters(&self) -> &'static [Parameter] {
self.parameters
}
}
pub trait Resource<I, O>: Sized {
const NAME: &'static str;
fn endpoints<B, R>() -> Vec<Endpoint<Self, B, R>>
where
B: RequestBody<BinaryBody = I>,
R: VisitResponse<BinaryWriter = O>;
}
pub trait RequestBody {
type BinaryBody;
fn accept<V>(self, visitor: V) -> Result<V::Output, Error>
where
V: VisitRequestBody<Self::BinaryBody>;
}
pub trait VisitRequestBody<T>: Sized {
type Output;
fn visit_empty(self) -> Result<Self::Output, Error> {
Err(Error::service_safe(
"unexpected empty request body",
InvalidArgument::new(),
))
}
fn visit_serializable<'de, D>(self, deserializer: D) -> Result<Self::Output, Error>
where
D: Deserializer<'de>,
D::Error: Into<Box<error::Error + Sync + Send>>,
{
let _ = deserializer;
Err(Error::service_safe(
"unexpected serializable request body",
InvalidArgument::new(),
))
}
fn visit_binary(self, body: T) -> Result<Self::Output, Error> {
let _ = body;
Err(Error::service_safe(
"unexpected binary request body",
InvalidArgument::new(),
))
}
}
pub trait Response<W> {
fn accept<V>(self, visitor: V) -> Result<V::Output, Error>
where
V: VisitResponse<BinaryWriter = W>;
}
pub trait VisitResponse {
type BinaryWriter;
type Output;
fn visit_empty(self) -> Result<Self::Output, Error>;
fn visit_serializable<T>(self, body: T) -> Result<Self::Output, Error>
where
T: Serialize + 'static;
fn visit_binary<T>(self, body: T) -> Result<Self::Output, Error>
where
T: WriteBody<Self::BinaryWriter> + 'static;
}
pub trait WriteBody<W> {
fn write_body(self, w: &mut W) -> Result<(), Error>;
}
impl<W> WriteBody<W> for Vec<u8>
where
W: Write,
{
fn write_body(self, w: &mut W) -> Result<(), Error> {
w.write_all(&self).map_err(Error::internal_safe)
}
}