Rust bindings to the Dropbox APIv2, generated by Stone from the official spec.
The Stone SDK and Dropbox API spec used to generate the code are in the stone
and dropbox-api-spec
submodules, respectively. Use git submodule init
and
git submodule update
to fetch them.
The generated code is checked in under src/generated
in order to simplify
building. To regenerate or update it, run python generate.py
. Doing so
requires a working Python environment and some dependencies. See the Stone
documentation for details.
This SDK is not yet official. What does this mean?
However, that said,
The routes (functions) come in two variants: sync, which do blocking network I/O and return their value directly; and async, which return futures.
The sync routes (in the dropbox_sdk::sync_routes
module) are enabled by
default or with the sync_routes
feature, and likewise the async ones are
in the dropbox_sdk::async_routes
module and can be enabled with the
async_routes
feature.
Additionally, if the sync_routes_default
feature is on (as it is by
default), the sync routes are available directly as dropbox_sdk::{namespace}
,
which matches the original structure before the async routes were added.
To actually use the API calls, you need a HTTP client -- all functions take a
type that implements HttpClient
as their first argument. This trait is
located at dropbox_sdk::client_trait::HttpClient
. Implement this trait and
pass it as the client argument.
If you don't want to implement your own, this SDK comes with an optional
default client that uses ureq
and rustls
. To use it, build with the
default_client
feature flag, and then there will be a set of clients in the
dropbox_sdk::default_client
module that you can use, corresponding to each of
the authentication types Dropbox uses (see below). The default client needs a
Dropbox API token; how you get one is up to you and your program. See the
programs under examples/ for examples, and see the helper code in
the oauth2 module.
Async clients can be implemented using a parallel set of traits located in the
dropbox_sdk::async_client_trait
module. A default implementation (which uses
reqwest
can be enabled with the default_async_client
feature and is located
at dropbox_sdk::default_async_client
.
The Dropbox API has a number of different authentication types. Each route
requires a HTTP client compatible with the specific authentication type needed.
The authentication type is designated by implementing a marker trait in
addition to the base HttpClient
trait: one of NoauthClient
,
UserAuthClient
, TeamAuthClient
, or AppAuthClient
.
The default client has implementations of all of these (except for
AppAuthClient
currently). They all share a common implementation and differ
only in which HTTP headers they add to the request.
If you only use a subset of the API, and you want to cut down on the compile
time, you can explicitly specify features corresponding to the namespaces you
need. For each namespace there is a corresponding feature dbx_{whatever}
. The
set of features can be updated if needed using the update_manifest.py
script.
An example that only needs the 'files' and 'users' namespaces:
[dependencies.dropbox-sdk]
version = "*"
default_features = false
features = ["dbx_files", "dbx_users"]
The tests are auto-generated from the spec as well, but unlike the main code,
are not checked in. Run python generate.py
to generate the tests, and cargo test
to run them.
The test generator starts by generating a reference Python SDK and loading that code. It then generates an instance of every type in the SDK and uses the Python code to serialize them to JSON. Then it emits Rust tests that contain the JSON as a string, deserialize it, assert that all fields contain the expected values, re-serialize it, deserialize it again, and assert the fields again. Thus we have reasonably good coverage of the serialization and deserialization logic that the Rust generator emits, checked against the Python implementation (which is what Dropbox uses server-side).
Some implementation notes, limitations, and TODOs:
dropbox_sdk::files::Metadata
for an example. Upcasting is
supported using generated From
implementations which either construct the
right enum variant or copy the subset of common fields.serde_derive
for the most part, and instead uses
manually-emitted serialization code. Previous work on this crate did attempt
to use serde_derive
, but the way the Dropbox API serializes unions
containing structs (by collapsing their fields into the union) isn't
supported by serde_derive
. It also took an extremely long time to compile
(~30 minutes for release build) and huge (~190MB) .rlib files. The
hand-written code is more versatile, compiles faster, and produces a smaller
binary, at the expense of making the generated source code much larger.std::future::ready()
,
which is then removed using now_or_never()
before being returned to
callers. Even though futures are passed around and async functions are used,
no executor is actually needed because of this.