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}