1use crate::{
2 error::{Error, Result},
3 resource::{DriveId, ErrorResponse, ItemId, OAuth2ErrorResponse},
4};
5use reqwest::{header, RequestBuilder, Response, StatusCode};
6use serde::{de, Deserialize};
7use url::PathSegmentsMut;
8
9#[derive(Clone, Debug)]
18pub struct DriveLocation {
19 inner: DriveLocationEnum,
20}
21
22#[derive(Clone, Debug)]
23enum DriveLocationEnum {
24 Me,
25 User(String),
26 Group(String),
27 Site(String),
28 Id(DriveId),
29}
30
31impl DriveLocation {
32 #[must_use]
37 pub fn me() -> Self {
38 Self {
39 inner: DriveLocationEnum::Me,
40 }
41 }
42
43 pub fn from_user(id_or_principal_name: impl Into<String>) -> Self {
48 Self {
49 inner: DriveLocationEnum::User(id_or_principal_name.into()),
50 }
51 }
52
53 pub fn from_group(group_id: impl Into<String>) -> Self {
58 Self {
59 inner: DriveLocationEnum::Group(group_id.into()),
60 }
61 }
62
63 pub fn from_site(site_id: impl Into<String>) -> Self {
68 Self {
69 inner: DriveLocationEnum::Site(site_id.into()),
70 }
71 }
72
73 #[must_use]
78 pub fn from_id(drive_id: DriveId) -> Self {
79 Self {
80 inner: DriveLocationEnum::Id(drive_id),
81 }
82 }
83}
84
85impl From<DriveId> for DriveLocation {
86 fn from(id: DriveId) -> Self {
87 Self::from_id(id)
88 }
89}
90
91#[derive(Clone, Copy, Debug)]
102pub struct ItemLocation<'a> {
103 inner: ItemLocationEnum<'a>,
104}
105
106#[derive(Clone, Copy, Debug)]
107enum ItemLocationEnum<'a> {
108 Path(&'a str),
109 Id(&'a str),
110 ChildOfId {
113 parent_id: &'a str,
114 child_name: &'a str,
115 },
116}
117
118impl<'a> ItemLocation<'a> {
119 #[must_use]
134 pub fn from_path(path: &'a str) -> Option<Self> {
135 if path == "/" {
136 Some(Self::root())
137 } else if path.starts_with('/')
138 && path[1..]
139 .split_terminator('/')
140 .all(|comp| !comp.is_empty() && FileName::new(comp).is_some())
141 {
142 Some(Self {
143 inner: ItemLocationEnum::Path(path),
144 })
145 } else {
146 None
147 }
148 }
149
150 #[must_use]
152 pub fn from_id(item_id: &'a ItemId) -> Self {
153 Self {
154 inner: ItemLocationEnum::Id(item_id.as_str()),
155 }
156 }
157
158 #[must_use]
160 pub fn root() -> Self {
161 Self {
162 inner: ItemLocationEnum::Path("/"),
163 }
164 }
165
166 #[must_use]
168 pub fn child_of_id(parent_id: &'a ItemId, child_name: &'a FileName) -> Self {
169 Self {
170 inner: ItemLocationEnum::ChildOfId {
171 parent_id: parent_id.as_str(),
172 child_name: child_name.as_str(),
173 },
174 }
175 }
176}
177
178impl<'a> From<&'a ItemId> for ItemLocation<'a> {
179 fn from(id: &'a ItemId) -> Self {
180 Self::from_id(id)
181 }
182}
183
184#[derive(Debug)]
186pub struct FileName(str);
187
188impl FileName {
189 pub fn new<S: AsRef<str> + ?Sized>(name: &S) -> Option<&Self> {
198 const INVALID_CHARS: &str = r#""*:<>?/\|"#;
199
200 let name = name.as_ref();
201 if !name.is_empty() && !name.contains(|c| INVALID_CHARS.contains(c)) {
202 Some(unsafe { &*(name as *const str as *const Self) })
203 } else {
204 None
205 }
206 }
207
208 #[must_use]
210 pub fn as_str(&self) -> &str {
211 &self.0
212 }
213}
214
215impl AsRef<str> for FileName {
216 fn as_ref(&self) -> &str {
217 self.as_str()
218 }
219}
220
221pub(crate) trait ApiPathComponent {
222 fn extend_into(&self, buf: &mut PathSegmentsMut);
223}
224
225impl ApiPathComponent for DriveLocation {
226 fn extend_into(&self, buf: &mut PathSegmentsMut) {
227 match &self.inner {
228 DriveLocationEnum::Me => buf.extend(&["me", "drive"]),
229 DriveLocationEnum::User(id) => buf.extend(&["users", id, "drive"]),
230 DriveLocationEnum::Group(id) => buf.extend(&["groups", id, "drive"]),
231 DriveLocationEnum::Site(id) => buf.extend(&["sites", id, "drive"]),
232 DriveLocationEnum::Id(id) => buf.extend(&["drives", id.as_str()]),
233 };
234 }
235}
236
237impl ApiPathComponent for ItemLocation<'_> {
238 fn extend_into(&self, buf: &mut PathSegmentsMut) {
239 match &self.inner {
240 ItemLocationEnum::Path("/") => buf.push("root"),
241 ItemLocationEnum::Path(path) => buf.push(&["root:", path, ":"].join("")),
242 ItemLocationEnum::Id(id) => buf.extend(&["items", id]),
243 ItemLocationEnum::ChildOfId {
244 parent_id,
245 child_name,
246 } => buf.extend(&["items", parent_id, "children", child_name]),
247 };
248 }
249}
250
251impl ApiPathComponent for str {
252 fn extend_into(&self, buf: &mut PathSegmentsMut) {
253 buf.push(self);
254 }
255}
256
257pub(crate) trait RequestBuilderTransformer {
258 fn trans(self, req: RequestBuilder) -> RequestBuilder;
259}
260
261pub(crate) trait RequestBuilderExt: Sized {
262 fn apply(self, trans: impl RequestBuilderTransformer) -> Self;
263}
264
265impl RequestBuilderExt for RequestBuilder {
266 fn apply(self, trans: impl RequestBuilderTransformer) -> Self {
267 trans.trans(self)
268 }
269}
270
271type BoxFuture<T> = std::pin::Pin<Box<dyn std::future::Future<Output = T> + Send + 'static>>;
272
273pub(crate) trait ResponseExt: Sized {
275 fn parse<T: de::DeserializeOwned>(self) -> BoxFuture<Result<T>>;
276 fn parse_optional<T: de::DeserializeOwned>(self) -> BoxFuture<Result<Option<T>>>;
277 fn parse_no_content(self) -> BoxFuture<Result<()>>;
278}
279
280impl ResponseExt for Response {
281 fn parse<T: de::DeserializeOwned>(self) -> BoxFuture<Result<T>> {
282 Box::pin(async move { Ok(handle_error_response(self).await?.json().await?) })
283 }
284
285 fn parse_optional<T: de::DeserializeOwned>(self) -> BoxFuture<Result<Option<T>>> {
286 Box::pin(async move {
287 match self.status() {
288 StatusCode::NOT_MODIFIED | StatusCode::ACCEPTED => Ok(None),
289 _ => Ok(Some(handle_error_response(self).await?.json().await?)),
290 }
291 })
292 }
293
294 fn parse_no_content(self) -> BoxFuture<Result<()>> {
295 Box::pin(async move {
296 handle_error_response(self).await?;
297 Ok(())
298 })
299 }
300}
301
302pub(crate) async fn handle_error_response(resp: Response) -> Result<Response> {
303 #[derive(Deserialize)]
304 struct Resp {
305 error: ErrorResponse,
306 }
307
308 let status = resp.status();
309 if status.is_success() || status.is_redirection() {
311 Ok(resp)
312 } else {
313 let retry_after = parse_retry_after_sec(&resp);
314 let resp: Resp = resp.json().await?;
315 Err(Error::from_error_response(status, resp.error, retry_after))
316 }
317}
318
319pub(crate) async fn handle_oauth2_error_response(resp: Response) -> Result<Response> {
320 let status = resp.status();
321 if status.is_success() {
322 Ok(resp)
323 } else {
324 let retry_after = parse_retry_after_sec(&resp);
325 let resp: OAuth2ErrorResponse = resp.json().await?;
326 Err(Error::from_oauth2_error_response(status, resp, retry_after))
327 }
328}
329
330fn parse_retry_after_sec(resp: &Response) -> Option<u32> {
335 resp.headers()
336 .get(header::RETRY_AFTER)?
337 .to_str()
338 .ok()?
339 .parse()
340 .ok()
341}