onedrive_api/lib.rs
1//! # onedrive-api
2//!
3//! `onedrive-api` crate provides middle-level HTTP APIs [`OneDrive`][one_drive] to the
4//! [OneDrive][ms_onedrive] API through [Microsoft Graph][ms_graph], and also [`Auth`][auth]
5//! with utilities for OAuth2.
6//!
7//! ## Example
8//! ```
9//! use onedrive_api::{OneDrive, FileName, DriveLocation, ItemLocation};
10//! use reqwest::Client;
11//!
12//! # async fn run() -> onedrive_api::Result<()> {
13//! let client = Client::new();
14//! let drive = OneDrive::new(
15//! "<...TOKEN...>", // Login token to Microsoft Graph.
16//! DriveLocation::me(),
17//! );
18//!
19//! let folder_item = drive
20//! .create_folder(
21//! ItemLocation::root(),
22//! FileName::new("test_folder").unwrap(),
23//! )
24//! .await?;
25//!
26//! drive
27//! .upload_small(
28//! folder_item.id.as_ref().unwrap(),
29//! &b"Hello, world"[..],
30//! )
31//! .await?;
32//!
33//! # Ok(())
34//! # }
35//! ```
36//!
37//! # Features
38//! - `beta`
39//!
40//! Most of Microsoft APIs used in this crate are stable.
41//! But there are also some beta APIs, which are subject to change and
42//! is not suggested to be used in production application.
43//! Microsoft references of beta APIs usually contain a `(beta)` suffix in title.
44//!
45//! To avoid breakage, we put beta APIs and related resources under feature gate `beta`.
46//! They may change to follow Microsoft API references **ANYTIME**,
47//! and do **NOT** follow the semantic version of this crate.
48//!
49//! Be carefully using it and **do NOT use it in production**.
50//!
51//! [ms_onedrive]: https://products.office.com/en-us/onedrive/online-cloud-storage
52//! [ms_graph]: https://docs.microsoft.com/graph/overview
53//! [one_drive]: ./struct.OneDrive.html
54//! [auth]: ./struct.Auth.html
55//! [api]: ./trait.Api.html
56//! [api_execute]: ./trait.Api.html#tymethod.execute
57//! [client]: ./trait.Client.html
58// #![deny(warnings)]
59#![deny(missing_debug_implementations)]
60#![deny(missing_docs)]
61use serde::{de, Serialize};
62
63mod auth;
64mod error;
65mod onedrive;
66pub mod option;
67pub mod resource;
68mod util;
69
70pub use self::{
71 auth::{Auth, ClientCredential, Permission, Tenant, TokenResponse},
72 error::{Error, Result},
73 onedrive::{
74 CopyProgressMonitor, ListChildrenFetcher, OneDrive, TrackChangeFetcher, UploadSession,
75 UploadSessionMeta,
76 },
77 resource::{DriveId, ItemId, Tag},
78 util::{DriveLocation, FileName, ItemLocation},
79};
80
81#[cfg(feature = "beta")]
82pub use self::onedrive::{CopyProgress, CopyStatus};
83
84/// The conflict resolution behavior for actions that create a new item.
85///
86/// # See also
87/// [Microsoft Docs](https://docs.microsoft.com/en-us/graph/api/resources/driveitem?view=graph-rest-1.0#instance-attributes)
88#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)]
89#[serde(rename_all = "camelCase")]
90pub enum ConflictBehavior {
91 /// Make the request fail. Usually cause HTTP 409 CONFLICT.
92 Fail,
93 /// **DANGER**: Replace the existing item.
94 Replace,
95 /// Rename the newly created item to another name.
96 ///
97 /// The new name is not specified and usually can be retrieved from the response.
98 Rename,
99}
100
101/// A half-open byte range `start..end` or `start..`.
102#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub struct ExpectRange {
104 /// The lower bound of the range (inclusive).
105 pub start: u64,
106 /// The optional upper bound of the range (exclusive).
107 pub end: Option<u64>,
108}
109
110impl<'de> de::Deserialize<'de> for ExpectRange {
111 fn deserialize<D: de::Deserializer<'de>>(
112 deserializer: D,
113 ) -> std::result::Result<Self, D::Error> {
114 struct Visitor;
115
116 impl de::Visitor<'_> for Visitor {
117 type Value = ExpectRange;
118
119 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
120 write!(f, "Expect Range")
121 }
122
123 fn visit_str<E: de::Error>(self, v: &str) -> std::result::Result<Self::Value, E> {
124 let parse = || -> Option<ExpectRange> {
125 let mut it = v.split('-');
126 let start = it.next()?.parse().ok()?;
127 let end = match it.next()? {
128 "" => None,
129 s => {
130 let end = s.parse::<u64>().ok()?.checked_add(1)?; // Exclusive.
131 if end <= start {
132 return None;
133 }
134 Some(end)
135 }
136 };
137 if it.next().is_some() {
138 return None;
139 }
140
141 Some(ExpectRange { start, end })
142 };
143 match parse() {
144 Some(v) => Ok(v),
145 None => Err(E::invalid_value(
146 de::Unexpected::Str(v),
147 &"`{lower}-` or `{lower}-{upper}`",
148 )),
149 }
150 }
151 }
152
153 deserializer.deserialize_str(Visitor)
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_range_parsing() {
163 let max = format!("0-{}", u64::MAX - 1);
164 let overflow = format!("0-{}", u64::MAX);
165 let cases = [
166 (
167 "42-196",
168 Some(ExpectRange {
169 start: 42,
170 end: Some(197),
171 }),
172 ), // [left, right)
173 (
174 "418-",
175 Some(ExpectRange {
176 start: 418,
177 end: None,
178 }),
179 ),
180 ("", None),
181 ("42-4", None),
182 ("-9", None),
183 ("-", None),
184 ("1-2-3", None),
185 ("0--2", None),
186 ("-1-2", None),
187 (
188 &max,
189 Some(ExpectRange {
190 start: 0,
191 end: Some(u64::MAX),
192 }),
193 ),
194 (&overflow, None),
195 ];
196
197 for &(s, ref expect) in &cases {
198 let ret = serde_json::from_str(&serde_json::to_string(s).unwrap());
199 assert_eq!(
200 ret.as_ref().ok(),
201 expect.as_ref(),
202 "Failed: Got {ret:?} on {s:?}",
203 );
204 }
205 }
206}