An open API service indexing awesome lists of open source software.

https://github.com/dropbox/dropbox-sdk-rust

Dropbox SDK for Rust
https://github.com/dropbox/dropbox-sdk-rust

Last synced: 7 months ago
JSON representation

Dropbox SDK for Rust

Awesome Lists containing this project

README

        

# Dropbox SDK for Rust

[![Crates.io](https://img.shields.io/crates/v/dropbox-sdk)](https://crates.io/crates/dropbox-sdk)
[![docs.rs](https://docs.rs/dropbox-sdk/badge.svg)](https://docs.rs/dropbox-sdk/)

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.

## Status of this SDK

This SDK is not yet official. What does this mean?
* There is no formal Dropbox support for the SDK at this point.
* Bugs may or may not get fixed.
* Not all SDK features may be implemented.

However, that said,
* The SDK is usable!
* We are happy to get feedback and/or pull requests from the community! See
[contributing](CONTRIBUTING.md) for more information.

## Sync and Async

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.

## HTTP Client

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/](examples/) for examples, and see the helper code in
the [oauth2](src/oauth2.rs) 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`.

## Authentication Types

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.

[authentication types]: https://www.dropbox.com/developers/reference/auth-types

## Feature Flags

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"]
```

## Tests

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).

## Miscellaneous

Some implementation notes, limitations, and TODOs:
* Stone allows structures to inherit from other structures and be polymorphic.
Rust doesn't have these paradigms, so instead this SDK represents
polymorphic parent structs as enums, and the inherited fields are put in all
variants. See `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.
* This code does not use `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.
* Types with constraints (such as strings with patterns or min/max lengths, or
integers with a range) do not check that the data being stored in them meets
the constraints.
* The sync routes and clients are actually implemented in terms of the async
client interfaces, but all the futures returned are `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.

## Happy Dropboxing!