1use serde::Serialize;
8use url::Url;
9
10use crate::client::{AccountClient, Client, RequestOptions, Response};
11use crate::error::Error;
12use crate::http::Method;
13use crate::operation::Operation;
14use crate::security::is_same_origin;
15
16impl Client {
17 pub async fn get(&self, path: &str) -> Result<Response, Error> {
19 self.execute(self.raw(Method::GET, path)?).await
20 }
21
22 pub async fn get_with(&self, path: &str, options: &RequestOptions) -> Result<Response, Error> {
24 self.execute(options.apply(self.raw(Method::GET, path)?))
25 .await
26 }
27
28 pub async fn post(&self, path: &str, body: &impl Serialize) -> Result<Response, Error> {
30 self.send_json(Method::POST, path, body, &RequestOptions::default())
31 .await
32 }
33
34 pub async fn post_with(
36 &self,
37 path: &str,
38 body: &impl Serialize,
39 options: &RequestOptions,
40 ) -> Result<Response, Error> {
41 self.send_json(Method::POST, path, body, options).await
42 }
43
44 pub async fn put(&self, path: &str, body: &impl Serialize) -> Result<Response, Error> {
46 self.send_json(Method::PUT, path, body, &RequestOptions::default())
47 .await
48 }
49
50 pub async fn put_with(
52 &self,
53 path: &str,
54 body: &impl Serialize,
55 options: &RequestOptions,
56 ) -> Result<Response, Error> {
57 self.send_json(Method::PUT, path, body, options).await
58 }
59
60 pub async fn patch(&self, path: &str, body: &impl Serialize) -> Result<Response, Error> {
62 self.send_json(Method::PATCH, path, body, &RequestOptions::default())
63 .await
64 }
65
66 pub async fn patch_with(
68 &self,
69 path: &str,
70 body: &impl Serialize,
71 options: &RequestOptions,
72 ) -> Result<Response, Error> {
73 self.send_json(Method::PATCH, path, body, options).await
74 }
75
76 pub async fn delete(&self, path: &str) -> Result<Response, Error> {
78 self.execute(self.raw(Method::DELETE, path)?).await
79 }
80
81 pub async fn delete_with(
83 &self,
84 path: &str,
85 options: &RequestOptions,
86 ) -> Result<Response, Error> {
87 self.execute(options.apply(self.raw(Method::DELETE, path)?))
88 .await
89 }
90
91 pub(crate) fn raw(&self, method: Method, path: &str) -> Result<Operation, Error> {
95 Ok(match self.absolute(path)? {
96 Some(url) => Operation::at(method, url),
97 None => Operation::raw(method, path.to_string()),
98 })
99 }
100
101 fn absolute(&self, path: &str) -> Result<Option<Url>, Error> {
105 if path.starts_with("https://") || path.starts_with("http://") {
106 let url = Url::parse(path)?;
107 if !url.username().is_empty() || url.password().is_some() {
108 Err(Error::usage(
109 "URL must not carry credentials; use an access token or a session token",
110 ))
111 } else if is_same_origin(&url, self.base_url()) {
112 Ok(Some(url))
113 } else {
114 Err(Error::usage(format!(
115 "URL must be on the Fizzy origin {}, got one on {}",
116 self.base_url().origin().ascii_serialization(),
117 url.origin().ascii_serialization()
118 )))
119 }
120 } else {
121 Ok(None)
122 }
123 }
124
125 async fn send_json(
126 &self,
127 method: Method,
128 path: &str,
129 body: &impl Serialize,
130 options: &RequestOptions,
131 ) -> Result<Response, Error> {
132 let mut operation = self.raw(method, path)?;
133 operation.json(body)?;
134 self.execute(options.apply(operation)).await
135 }
136}
137
138impl AccountClient {
139 pub async fn get(&self, path: &str) -> Result<Response, Error> {
141 self.client().get(&self.scoped(path)).await
142 }
143
144 pub async fn get_with(&self, path: &str, options: &RequestOptions) -> Result<Response, Error> {
146 self.client().get_with(&self.scoped(path), options).await
147 }
148
149 pub async fn post(&self, path: &str, body: &impl Serialize) -> Result<Response, Error> {
151 self.client().post(&self.scoped(path), body).await
152 }
153
154 pub async fn post_with(
156 &self,
157 path: &str,
158 body: &impl Serialize,
159 options: &RequestOptions,
160 ) -> Result<Response, Error> {
161 self.client()
162 .post_with(&self.scoped(path), body, options)
163 .await
164 }
165
166 pub async fn put(&self, path: &str, body: &impl Serialize) -> Result<Response, Error> {
168 self.client().put(&self.scoped(path), body).await
169 }
170
171 pub async fn put_with(
173 &self,
174 path: &str,
175 body: &impl Serialize,
176 options: &RequestOptions,
177 ) -> Result<Response, Error> {
178 self.client()
179 .put_with(&self.scoped(path), body, options)
180 .await
181 }
182
183 pub async fn patch(&self, path: &str, body: &impl Serialize) -> Result<Response, Error> {
185 self.client().patch(&self.scoped(path), body).await
186 }
187
188 pub async fn patch_with(
190 &self,
191 path: &str,
192 body: &impl Serialize,
193 options: &RequestOptions,
194 ) -> Result<Response, Error> {
195 self.client()
196 .patch_with(&self.scoped(path), body, options)
197 .await
198 }
199
200 pub async fn delete(&self, path: &str) -> Result<Response, Error> {
202 self.client().delete(&self.scoped(path)).await
203 }
204
205 pub async fn delete_with(
207 &self,
208 path: &str,
209 options: &RequestOptions,
210 ) -> Result<Response, Error> {
211 self.client().delete_with(&self.scoped(path), options).await
212 }
213
214 pub async fn get_all(&self, path: &str) -> Result<Vec<serde_json::Value>, Error> {
216 self.client().get_all(&self.scoped(path)).await
217 }
218
219 pub async fn get_all_with_limit(
222 &self,
223 path: &str,
224 limit: usize,
225 ) -> Result<Vec<serde_json::Value>, Error> {
226 self.client()
227 .get_all_with_limit(&self.scoped(path), limit)
228 .await
229 }
230
231 fn scoped(&self, path: &str) -> String {
234 if path.starts_with("https://") || path.starts_with("http://") {
235 path.to_string()
236 } else {
237 format!(
238 "/{}/{}",
239 crate::route::encode(self.account_id()),
240 path.trim_start_matches('/')
241 )
242 }
243 }
244}