-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #150 from wfraser/async-mode
implement optional async support and remove nested result type in response
- Loading branch information
Showing
89 changed files
with
13,579 additions
and
6,428 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
[package] | ||
name = "dropbox-sdk" | ||
version = "0.18.1" | ||
version = "0.19.0-beta1" | ||
authors = ["Bill Fraser <[email protected]>"] | ||
edition = "2018" | ||
description = "Rust bindings to the Dropbox API, generated by Stone from the official spec." | ||
|
@@ -12,20 +12,34 @@ readme = "README.md" | |
|
||
[package.metadata] | ||
# Keep this at least 1 year old. | ||
msrv = "1.65.0" # Nov 3, 2022 | ||
# (or not... 1.75 is required for "-> impl Trait" sadly) | ||
msrv = "1.75.0" # Dec 28, 2023 | ||
|
||
[dependencies] | ||
async-lock = "3.3.0" | ||
atty = "0.2.14" | ||
base64 = "0.22" | ||
bytes = "1.6.0" | ||
log = "0.4" | ||
ring = "0.17" | ||
serde = { version = "1.0", features = ["derive"] } | ||
serde_json = "1.0" | ||
thiserror = "1.0" | ||
url = "2.1" | ||
|
||
[dependencies.futures] | ||
version = "0.3.30" | ||
default-features = false | ||
features = ["std"] | ||
|
||
[dependencies.reqwest] | ||
version = "0.12.2" | ||
optional = true | ||
default-features = false | ||
features = ["http2", "rustls-tls", "stream"] | ||
|
||
[dependencies.ureq] | ||
version = "2.0.0" | ||
version = "2.5.0" | ||
optional = true | ||
default-features = false | ||
features = ["tls"] | ||
|
@@ -36,6 +50,15 @@ chrono = "0.4" | |
parallel_reader = "0.1" | ||
threadpool = "1.8" | ||
|
||
[dev-dependencies.tokio] | ||
version = "1.37.0" | ||
features = ["rt-multi-thread", "macros", "io-std"] | ||
|
||
[dev-dependencies.tokio-util] | ||
version = "0.7.10" | ||
default-features = false | ||
features = ["compat"] | ||
|
||
[[example]] | ||
name = "demo" | ||
required-features = ["dbx_files", "default_client"] | ||
|
@@ -44,6 +67,10 @@ required-features = ["dbx_files", "default_client"] | |
name = "large-file-upload" | ||
required-features = ["dbx_files", "default_client"] | ||
|
||
[[example]] | ||
name = "demo-async" | ||
required-features = ["dbx_files", "default_async_client"] | ||
|
||
[features] | ||
# dbx_* features each correspond to one Stone spec file. | ||
# The lists of dependencies must be kept in sync with the 'import' statements in them. | ||
|
@@ -68,12 +95,24 @@ dbx_team_policies = [] | |
dbx_users = ["dbx_common", "dbx_team_common", "dbx_team_policies", "dbx_users_common"] | ||
dbx_users_common = ["dbx_common"] | ||
|
||
default_client = ["ureq"] | ||
default_async_client = ["async_routes", "dep:reqwest"] | ||
default_client = ["sync_routes", "sync_routes_default", "dep:ureq"] | ||
|
||
# Enable unstable ("preview") API routes. | ||
unstable = [] | ||
|
||
# Include everything by default. | ||
# Enable sync routes under `dropbox_sdk::routes::{namespace}` | ||
sync_routes = [] | ||
|
||
# Enable async routes under `dropbox_sdk::async_routes::{namespace}` | ||
async_routes = [] | ||
|
||
# Re-export the sync routes as `dropbox_sdk::{namsepace}` directly (matches pre-v0.19 structure). | ||
# If disabled, export the async routes there instead. | ||
sync_routes_default = ["sync_routes"] | ||
|
||
# Include all namespaces by default. | ||
# Enable sync default client, sync routes, and make the sync routes default, to match pre-v0.19. | ||
default = [ | ||
"dbx_account", | ||
"dbx_async", | ||
|
@@ -96,6 +135,8 @@ default = [ | |
"dbx_users", | ||
"dbx_users_common", | ||
"default_client", | ||
"sync_routes", | ||
"sync_routes_default", | ||
] | ||
|
||
[package.metadata.docs.rs] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
#![deny(rust_2018_idioms)] | ||
|
||
//! This example illustrates a few basic Dropbox API operations: getting an OAuth2 token, listing | ||
//! the contents of a folder recursively, and fetching a file given its path. | ||
use tokio_util::compat::FuturesAsyncReadCompatExt; | ||
use dropbox_sdk::default_async_client::{NoauthDefaultClient, UserAuthDefaultClient}; | ||
use dropbox_sdk::async_routes::files; | ||
|
||
enum Operation { | ||
Usage, | ||
List(String), | ||
Download(String), | ||
Stat(String), | ||
} | ||
|
||
fn parse_args() -> Operation { | ||
let mut ctor: Option<fn(String) -> Operation> = None; | ||
for arg in std::env::args().skip(1) { | ||
match arg.as_str() { | ||
"--help" | "-h" => return Operation::Usage, | ||
"--list" => { | ||
ctor = Some(Operation::List); | ||
} | ||
"--download" => { | ||
ctor = Some(Operation::Download); | ||
} | ||
"--stat" => { | ||
ctor = Some(Operation::Stat); | ||
} | ||
path if path.starts_with('/') => { | ||
return if let Some(ctor) = ctor { | ||
ctor(arg) | ||
} else { | ||
eprintln!("Either --download or --list must be specified"); | ||
Operation::Usage | ||
}; | ||
} | ||
_ => { | ||
eprintln!("Unrecognized option {arg:?}"); | ||
eprintln!(); | ||
return Operation::Usage; | ||
} | ||
} | ||
} | ||
Operation::Usage | ||
} | ||
|
||
#[tokio::main] | ||
async fn main() { | ||
env_logger::init(); | ||
|
||
let op = parse_args(); | ||
|
||
if let Operation::Usage = op { | ||
eprintln!("usage: {} [option]", std::env::args().next().unwrap()); | ||
eprintln!(" options:"); | ||
eprintln!(" --help | -h view this text"); | ||
eprintln!(" --download <path> copy the contents of <path> to stdout"); | ||
eprintln!(" --list <path> recursively list all files under <path>"); | ||
eprintln!(" --stat <path> list all metadata of <path>"); | ||
eprintln!(); | ||
eprintln!(" If a Dropbox OAuth token is given in the environment variable"); | ||
eprintln!(" DBX_OAUTH_TOKEN, it will be used, otherwise you will be prompted for"); | ||
eprintln!(" authentication interactively."); | ||
std::process::exit(1); | ||
} | ||
|
||
let mut auth = dropbox_sdk::oauth2::get_auth_from_env_or_prompt(); | ||
if auth.save().is_none() { | ||
auth.obtain_access_token_async(NoauthDefaultClient::default()).await.unwrap(); | ||
eprintln!("Next time set these environment variables to reuse this authorization:"); | ||
eprintln!(" DBX_CLIENT_ID={}", auth.client_id()); | ||
eprintln!(" DBX_OAUTH={}", auth.save().unwrap()); | ||
} | ||
let client = UserAuthDefaultClient::new(auth); | ||
|
||
match op { | ||
Operation::Usage => (), // handled above | ||
Operation::Download(path) => { | ||
eprintln!("Copying file to stdout: {}", path); | ||
eprintln!(); | ||
|
||
match files::download(&client, &files::DownloadArg::new(path), None, None).await { | ||
Ok(result) => { | ||
match tokio::io::copy( | ||
&mut result.body.expect("there must be a response body") | ||
.compat(), | ||
&mut tokio::io::stdout(), | ||
).await { | ||
Ok(n) => { | ||
eprintln!("Downloaded {n} bytes"); | ||
} | ||
Err(e) => { | ||
eprintln!("I/O error: {e}"); | ||
} | ||
} | ||
} | ||
Err(e) => { | ||
eprintln!("Error from files/download: {e}"); | ||
} | ||
} | ||
} | ||
Operation::List(mut path) => { | ||
eprintln!("Listing recursively: {path}"); | ||
|
||
// Special case: the root folder is empty string. All other paths need to start with '/'. | ||
if path == "/" { | ||
path.clear(); | ||
} | ||
|
||
let mut result = match files::list_folder( | ||
&client, | ||
&files::ListFolderArg::new(path).with_recursive(true), | ||
).await { | ||
Ok(result) => result, | ||
Err(e) => { | ||
eprintln!("Error from files/list_folder: {e}"); | ||
return; | ||
} | ||
}; | ||
|
||
let mut num_entries = result.entries.len(); | ||
let mut num_pages = 1; | ||
|
||
loop { | ||
for entry in result.entries { | ||
match entry { | ||
files::Metadata::Folder(entry) => { | ||
println!("Folder: {}", entry.path_display.unwrap_or(entry.name)); | ||
} | ||
files::Metadata::File(entry) => { | ||
println!("File: {}", entry.path_display.unwrap_or(entry.name)); | ||
} | ||
files::Metadata::Deleted(entry) => { | ||
panic!("unexpected deleted entry: {:?}", entry); | ||
} | ||
} | ||
} | ||
|
||
if !result.has_more { | ||
break; | ||
} | ||
|
||
result = match files::list_folder_continue( | ||
&client, | ||
&files::ListFolderContinueArg::new(result.cursor), | ||
).await { | ||
Ok(result) => { | ||
num_pages += 1; | ||
num_entries += result.entries.len(); | ||
result | ||
} | ||
Err(e) => { | ||
eprintln!("Error from files/list_folder_continue: {e}"); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
eprintln!("{num_entries} entries from {num_pages} result pages"); | ||
} | ||
Operation::Stat(path) => { | ||
eprintln!("listing metadata for: {path}"); | ||
|
||
let arg = files::GetMetadataArg::new(path) | ||
.with_include_media_info(true) | ||
.with_include_deleted(true) | ||
.with_include_has_explicit_shared_members(true); | ||
|
||
match files::get_metadata(&client, &arg).await { | ||
Ok(result) => println!("{result:#?}"), | ||
Err(e) => eprintln!("Error from files/get_metadata: {e}"), | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.