payjp_client_core/
request.rs1use std::fmt::Display;
2use std::marker::PhantomData;
3
4use bytes::Bytes;
5use miniserde::json::from_str;
6use serde::Serialize;
7
8use crate::request_strategy::RequestStrategy;
9use crate::ConfigOverride;
10
11#[derive(Debug, Copy, Clone)]
13pub enum PayjpMethod {
14 Get,
16 Post,
18 Delete,
20}
21
22pub trait PayjpClientErr {
25 fn deserialize_err(msg: impl Display) -> Self;
28}
29
30pub trait PayjpClient {
33 type Err: PayjpClientErr;
36
37 fn execute(
39 &self,
40 req: CustomizedPayjpRequest,
41 ) -> impl std::future::Future<Output = Result<Bytes, Self::Err>>;
42}
43
44pub trait BlockingClient {
47 type Err: PayjpClientErr;
49
50 fn execute(&self, req: CustomizedPayjpRequest) -> Result<Bytes, Self::Err>;
52}
53
54pub trait PayjpRequest {
56 type Output;
58
59 fn build(&self) -> RequestBuilder;
62
63 fn customize(&self) -> CustomizablePayjpRequest<Self::Output> {
65 CustomizablePayjpRequest::new(self.build())
66 }
67}
68
69#[derive(Debug)]
72pub struct CustomizablePayjpRequest<T> {
73 inner: CustomizedPayjpRequest,
74 _output: PhantomData<T>,
75}
76
77#[derive(Debug)]
79pub struct CustomizedPayjpRequest {
80 request: RequestBuilder,
81 config_override: ConfigOverride,
82}
83
84impl CustomizedPayjpRequest {
85 pub fn into_pieces(self) -> (RequestBuilder, ConfigOverride) {
87 (self.request, self.config_override)
88 }
89}
90
91impl<T> CustomizablePayjpRequest<T> {
92 fn new(req_builder: RequestBuilder) -> Self {
93 Self {
94 _output: PhantomData,
95 inner: CustomizedPayjpRequest {
96 request: req_builder,
97 config_override: ConfigOverride::new(),
98 },
99 }
100 }
101
102 pub fn request_strategy(mut self, strategy: RequestStrategy) -> Self {
105 self.inner.config_override.request_strategy = Some(strategy);
106 self
107 }
108}
109
110impl<T: miniserde::Deserialize> CustomizablePayjpRequest<T> {
111 pub async fn send<C: PayjpClient>(self, client: &C) -> Result<T, C::Err> {
113 let bytes = client.execute(self.inner).await?;
114 deserialize_bytes(bytes)
115 }
116
117 pub fn send_blocking<C: BlockingClient>(self, client: &C) -> Result<T, C::Err> {
119 let bytes = client.execute(self.inner)?;
120 deserialize_bytes(bytes)
121 }
122}
123
124fn deserialize_bytes<T: miniserde::Deserialize, Err: PayjpClientErr>(
125 bytes: Bytes,
126) -> Result<T, Err> {
127 let str = std::str::from_utf8(bytes.as_ref())
128 .map_err(|_| Err::deserialize_err("Response was not valid UTF-8"))?;
129 from_str(str).map_err(|_| Err::deserialize_err("error deserializing request data"))
130}
131
132#[derive(Debug)]
134pub struct RequestBuilder {
135 pub query: Option<String>,
137 pub body: Option<String>,
139 pub path: String,
141 pub method: PayjpMethod,
143}
144
145impl RequestBuilder {
146 pub fn new(method: PayjpMethod, path: impl Into<String>) -> Self {
148 Self { path: path.into(), method, query: None, body: None }
149 }
150
151 #[allow(clippy::missing_panics_doc)]
153 pub fn query<P: Serialize>(mut self, params: &P) -> Self {
154 self.query = Some(serde_qs::to_string(params).expect("valid serialization"));
155 self
156 }
157
158 #[allow(clippy::missing_panics_doc)]
160 pub fn form<F: Serialize>(mut self, form: &F) -> Self {
161 self.body = Some(serde_qs::to_string(form).expect("valid serialization"));
162 self
163 }
164
165 pub fn customize<T>(self) -> CustomizablePayjpRequest<T> {
168 CustomizablePayjpRequest::new(self)
169 }
170}