use serde::de::DeserializeOwned;
use serde::Serialize;
use typeway_core::*;
use crate::error::ClientError;
pub trait CallEndpoint {
type Args;
type Response;
fn method() -> http::Method;
fn build_path(args: &Self::Args) -> String;
fn request_body(args: &Self::Args) -> Option<Result<Vec<u8>, ClientError>>;
fn parse_response(bytes: &[u8]) -> Result<Self::Response, ClientError>;
}
pub trait BuildPath {
fn build_path(captures: &Self, pattern: &str) -> String;
}
impl BuildPath for () {
fn build_path(_: &(), pattern: &str) -> String {
pattern.to_string()
}
}
macro_rules! impl_build_path {
($($idx:tt : $T:ident),+) => {
impl<$($T: std::fmt::Display,)+> BuildPath for ($($T,)+) {
fn build_path(captures: &Self, pattern: &str) -> String {
let mut result = pattern.to_string();
$(
result = result.replacen("{}", &captures.$idx.to_string(), 1);
)+
result
}
}
};
}
impl_build_path!(0: A);
impl_build_path!(0: A, 1: B);
impl_build_path!(0: A, 1: B, 2: C);
impl_build_path!(0: A, 1: B, 2: C, 3: D);
impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E);
impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
macro_rules! impl_call_bodyless {
($Method:ty) => {
impl<P, Res, Q, Err> CallEndpoint for Endpoint<$Method, P, NoBody, Res, Q, Err>
where
P: PathSpec + ExtractPath,
P::Captures: BuildPath,
Res: DeserializeOwned,
{
type Args = P::Captures;
type Response = Res;
fn method() -> http::Method {
<$Method as HttpMethod>::METHOD
}
fn build_path(args: &Self::Args) -> String {
BuildPath::build_path(args, &P::pattern())
}
fn request_body(_args: &Self::Args) -> Option<Result<Vec<u8>, ClientError>> {
None
}
fn parse_response(bytes: &[u8]) -> Result<Self::Response, ClientError> {
serde_json::from_slice(bytes).map_err(|e| ClientError::Deserialize(e.to_string()))
}
}
};
}
impl_call_bodyless!(Get);
impl_call_bodyless!(Delete);
impl_call_bodyless!(Head);
impl_call_bodyless!(Options);
macro_rules! impl_call_with_body {
($Method:ty) => {
impl<P, Req, Res, Q, Err> CallEndpoint for Endpoint<$Method, P, Req, Res, Q, Err>
where
P: PathSpec + ExtractPath,
P::Captures: BuildPath,
Req: Serialize,
Res: DeserializeOwned,
{
type Args = (P::Captures, Req);
type Response = Res;
fn method() -> http::Method {
<$Method as HttpMethod>::METHOD
}
fn build_path(args: &Self::Args) -> String {
BuildPath::build_path(&args.0, &P::pattern())
}
fn request_body(args: &Self::Args) -> Option<Result<Vec<u8>, ClientError>> {
Some(serde_json::to_vec(&args.1).map_err(|e| ClientError::Serialize(e.to_string())))
}
fn parse_response(bytes: &[u8]) -> Result<Self::Response, ClientError> {
serde_json::from_slice(bytes).map_err(|e| ClientError::Deserialize(e.to_string()))
}
}
};
}
impl_call_with_body!(Post);
impl_call_with_body!(Put);
impl_call_with_body!(Patch);