1use serde::de::DeserializeOwned;
4use serde::Serialize;
5
6use typeway_core::*;
7
8use crate::error::ClientError;
9
10pub trait CallEndpoint {
13 type Args;
15
16 type Response;
18
19 fn method() -> http::Method;
21
22 fn build_path(args: &Self::Args) -> String;
24
25 fn request_body(args: &Self::Args) -> Option<Result<Vec<u8>, ClientError>>;
27
28 fn parse_response(bytes: &[u8]) -> Result<Self::Response, ClientError>;
30}
31
32pub trait BuildPath {
38 fn build_path(captures: &Self, pattern: &str) -> String;
39}
40
41impl BuildPath for () {
42 fn build_path(_: &(), pattern: &str) -> String {
43 pattern.to_string()
44 }
45}
46
47macro_rules! impl_build_path {
48 ($($idx:tt : $T:ident),+) => {
49 impl<$($T: std::fmt::Display,)+> BuildPath for ($($T,)+) {
50 fn build_path(captures: &Self, pattern: &str) -> String {
51 let mut result = pattern.to_string();
52 $(
53 result = result.replacen("{}", &captures.$idx.to_string(), 1);
54 )+
55 result
56 }
57 }
58 };
59}
60
61impl_build_path!(0: A);
62impl_build_path!(0: A, 1: B);
63impl_build_path!(0: A, 1: B, 2: C);
64impl_build_path!(0: A, 1: B, 2: C, 3: D);
65impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E);
66impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F);
67impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G);
68impl_build_path!(0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G, 7: H);
69
70macro_rules! impl_call_bodyless {
75 ($Method:ty) => {
76 impl<P, Res, Q, Err> CallEndpoint for Endpoint<$Method, P, NoBody, Res, Q, Err>
77 where
78 P: PathSpec + ExtractPath,
79 P::Captures: BuildPath,
80 Res: DeserializeOwned,
81 {
82 type Args = P::Captures;
83 type Response = Res;
84
85 fn method() -> http::Method {
86 <$Method as HttpMethod>::METHOD
87 }
88
89 fn build_path(args: &Self::Args) -> String {
90 BuildPath::build_path(args, &P::pattern())
91 }
92
93 fn request_body(_args: &Self::Args) -> Option<Result<Vec<u8>, ClientError>> {
94 None
95 }
96
97 fn parse_response(bytes: &[u8]) -> Result<Self::Response, ClientError> {
98 serde_json::from_slice(bytes).map_err(|e| ClientError::Deserialize(e.to_string()))
99 }
100 }
101 };
102}
103
104impl_call_bodyless!(Get);
105impl_call_bodyless!(Delete);
106impl_call_bodyless!(Head);
107impl_call_bodyless!(Options);
108
109macro_rules! impl_call_with_body {
115 ($Method:ty) => {
116 impl<P, Req, Res, Q, Err> CallEndpoint for Endpoint<$Method, P, Req, Res, Q, Err>
117 where
118 P: PathSpec + ExtractPath,
119 P::Captures: BuildPath,
120 Req: Serialize,
121 Res: DeserializeOwned,
122 {
123 type Args = (P::Captures, Req);
124 type Response = Res;
125
126 fn method() -> http::Method {
127 <$Method as HttpMethod>::METHOD
128 }
129
130 fn build_path(args: &Self::Args) -> String {
131 BuildPath::build_path(&args.0, &P::pattern())
132 }
133
134 fn request_body(args: &Self::Args) -> Option<Result<Vec<u8>, ClientError>> {
135 Some(serde_json::to_vec(&args.1).map_err(|e| ClientError::Serialize(e.to_string())))
136 }
137
138 fn parse_response(bytes: &[u8]) -> Result<Self::Response, ClientError> {
139 serde_json::from_slice(bytes).map_err(|e| ClientError::Deserialize(e.to_string()))
140 }
141 }
142 };
143}
144
145impl_call_with_body!(Post);
146impl_call_with_body!(Put);
147impl_call_with_body!(Patch);