# What is Ditto?
Ditto is a cross-platform, peer-to-peer database that allows apps to sync **with
and without** internet connectivity.
Install Ditto into your application, then use the APIs to read and write data
into its storage system, and it will then automatically sync any changes to
other devices.
Unlike other synchronization solutions, Ditto is designed for "peer-to-peer"
scenarios where it can directly communicate with other devices even without an
Internet connection.
Additionally, Ditto automatically manages the complexity of using multiple
network transports, like Bluetooth, P2P Wi-Fi, and Local Area Network, to find
and connect to other devices and then synchronize any changes.
# Ditto Platform Docs
Visit <https://ditto.com/link/docs> to learn about the full Ditto platform,
including multi-language SDKs, the Ditto Cloud offering, and more.
Rust developers should be sure to check out these essential topics:
- [Ditto Edge Sync Platform Basics][000]
- [Mesh Networking 101][001]
- [Data-Handling Essentials][002]
- [Ditto Query Language (DQL)][003]
[000]: https://ditto.com/link/basic-about
[001]: https://ditto.com/link/basic-mesh-networking-101
[002]: https://ditto.com/link/basic-data-handling-essentials
[003]: https://ditto.com/link/dql
# Development Quickstart
Ditto offers a "development" authentication mode that lets you start playing and
developing with Ditto without any authentication hassle.
## Online Development Quickstart (Fastest)
- [Visit our credentials docs to learn how to get your Ditto Database ID and Development Token][100]
[100]: https://ditto.com/link/get-started-sync-credentials
```rust,no_run
# // To actually run this doctest, insert valid values into the constants below and
# // then remove the `no_run` annotation.
# use dittolive_ditto::fs::TempRoot;
use dittolive_ditto::prelude::*;
use dittolive_ditto::identity::get_development_provider;
const YOUR_SERVER_URL: &str = "replace with your server URL";
const YOUR_DB_ID: &str = "replace with your database ID";
const YOUR_DEVELOPMENT_AUTH_TOKEN: &str = "replace with your authentication token";
#[tokio::main]
async fn main() -> anyhow::Result<()> {
# let _root = TempRoot::new();
// Online development (syncs with ditto cloud)
let connect = DittoConfigConnect::Server { url: YOUR_SERVER_URL.parse().unwrap() };
let config = DittoConfig::new(YOUR_DB_ID, connect);
# let config = config.with_persistence_directory(_root.root_path());
// Use the async or sync constructor
let ditto = Ditto::open_sync(config)?;
// let ditto = Ditto::open(config).await?; // async constructor
// Register an expiration handler for authentication (required in Server mode)
let auth = ditto.auth().expect("Auth is available in Server mode");
auth.set_expiration_handler(async |ditto: &Ditto, _duration_remaining| {
let auth = ditto.auth().expect("Auth is available in Server mode");
// Testing/development using ditto cloud. NOT FOR PRODUCTION USE
if let Err(e) = auth.login(
YOUR_DEVELOPMENT_AUTH_TOKEN,
&get_development_provider(),
) {
eprintln!("Login failed: {e}");
}
});
// Start syncing with peers
ditto.sync().start()?;
Ok(())
}
```
## Offline Development Quickstart (Requires Offline License Token)
- Contact Ditto support to obtain an offline license token.
```rust
# use dittolive_ditto::fs::TempRoot;
use dittolive_ditto::prelude::*;
const YOUR_DB_ID: &str = "replace with your database ID";
const YOUR_OFFLINE_LICENSE_TOKEN: &str = "";
#[tokio::main]
async fn main() -> anyhow::Result<()> {
# let _root = TempRoot::new();
// Testing/Development using Small Peers only. NOT FOR PRODUCTION USE
let connect = DittoConfigConnect::SmallPeersOnly { private_key: None };
let config = DittoConfig::new(YOUR_DB_ID, connect);
# let config = config.with_persistence_directory(_root.root_path());
// Use the async or sync constructor
let ditto = Ditto::open_sync(config)?;
// let ditto = Ditto::open(config).await?; // async constructor
# ditto.set_license_from_env("DITTO_LICENSE")?;
# if false {
ditto.set_offline_only_license_token(YOUR_OFFLINE_LICENSE_TOKEN)?;
# }
// Start syncing with peers
ditto.sync().start()?;
Ok(())
}
```
## Write data using Ditto Query Language (DQL)
The preferred method to write data to Ditto is by using DQL.
To do this, we'll first access the Ditto [`Store`], then
execute a DQL insert statement.
[`Store`]: crate::store::Store
```rust
use dittolive_ditto::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Car {
color: String,
make: String,
}
async fn dql_insert_car(ditto: &Ditto, car: &Car) -> anyhow::Result<()> {
let store = ditto.store();
let query_result = store.execute((
// `cars` is the collection name
"INSERT INTO cars DOCUMENTS (:newCar)",
serde_json::json!({
"newCar": car
})
)).await?;
// Optional: See the count of items inserted
println!("Inserted {} item(s)", query_result.item_count());
// Optional: Inspect each item that was inserted
for query_item in query_result.iter() {
println!("Inserted: {}", query_item.json_string());
}
Ok(())
}
// To call:
async fn call_dql_insert(ditto: &Ditto) -> anyhow::Result<()> {
let my_car = Car {
color: "blue".to_string(),
make: "ford".to_string(),
};
dql_insert_car(ditto, &my_car).await?;
Ok(())
}
#
# #[tokio::main]
# async fn main() -> anyhow::Result<()> {
# let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
# call_dql_insert(&ditto).await
# }
```
- See the [DQL INSERT documentation][200] for more info on DQL inserts
- See [`QueryResult`] and [`QueryResultItem`] to learn about the returned values
- Tip: Make sure you have [`serde`] added to your `Cargo.toml` with the `derive` feature
- Tip: Make sure you have [`serde_json`] added to your `Cargo.toml`
```toml
# Cargo.toml
[dependencies]
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
```
[`QueryResult`]: crate::dql::QueryResult
[`QueryResultItem`]: crate::dql::QueryResultItem
[200]: https://ditto.com/link/dql-insert
[`serde`]: https://docs.rs/serde
[`serde_json`]: https://docs.rs/serde_json
## Read data using DQL
```rust
use dittolive_ditto::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct Car {
color: String,
make: String,
}
async fn dql_select_cars(ditto: &Ditto, color: &str) -> anyhow::Result<Vec<Car>> {
let store = ditto.store();
let query_result = store.execute((
"SELECT * FROM cars WHERE color = :myColor",
serde_json::json!({
"myColor": color
})
)).await?;
let cars = query_result.iter()
.map(|query_item| query_item.deserialize_value::<Car>())
.collect::<Result<Vec<Car>, _>>()?;
Ok(cars)
}
// To call:
async fn call_dql_select(ditto: &Ditto) -> anyhow::Result<()> {
let cars: Vec<Car> = dql_select_cars(ditto, "blue").await?;
Ok(())
}
# #[tokio::main]
# async fn main() -> anyhow::Result<()> {
# let (_root, ditto) = dittolive_ditto::doctest_helpers::doctest_ditto();
# call_dql_select(&ditto).await
# }
```
- See the [DQL SELECT documentation][300] for more info on DQL selects
[300]: https://ditto.com/link/dql-select
<details>
<summary><h1 id="notes-about-using-a-rust-wrapped-c-library-advanced">
Notes about using a Rust-wrapped C library (advanced)
</h1></summary>
**Please note**: this crate uses sane defaults that should "just work".
These notes should not be required to get started with Ditto, instead they're
meant for people interested in more advanced use-cases such as dynamically
linking or pre-downloading the companion C-library artifact.
Ditto's core functionality is released and packaged as a C library, which is
then imported into Rust via the `dittolive-ditto-sys` crate.
## Downloading the companion binary artifact
This crate will, at build time, download the appropriate binary artifact from
`https://software.ditto.live/rust/Ditto/<version>/<target>/release/[lib]dittoffi.{a,so,dylib,dll,lib}`
- For example: <https://software.ditto.live/rust/Ditto/5.0.0/aarch64-apple-darwin/release/libdittoffi.dylib>
If you wish to avoid this, you will have to do it yourself:
1. Download the proper binary artifact;
1. Instruct `::dittolive-ditto-sys`' `build.rs` script to use it by setting the
`DITTOFFI_SEARCH_PATH` environment variable appropriately (using an absolute
path is recommended).
More precisely, the library search resolution order is as follows:
1. `$DITTOFFI_SEARCH_PATH` (if set)
1. The current working directory (`$PWD`)
1. Best effort will be made to search `$CARGO_TARGET_DIR` and `$CARGO_TARGET_DIR/deps`, this is imprecise as it must be derived from `$OUT_DIR`.
1. `$OUT_DIR` (e.g. `${CARGO_TARGET_DIR}/<profile>/build/dittolive-ditto-sys-.../out`)
1. When using dynamic linking, host built-in defaults (e.g. `/usr/lib`, `/lib`, `/usr/local/lib`, `$HOME/lib`) controlled by (system) linker setup.
If the library artifact is not found at any of these locations, the build script
will attempt its own download into the `$OUT_DIR` (and use that path).
## Linking
C linkage is typically accompanied with some idiosyncrasies, such as symbol
conflicts, or path resolution errors.
The first question you should answer is whether you want your application to
use **static** or **dynamic** linking to access the Ditto library.
### Statically linking `libdittoffi` (default, recommended)
This happens whenever the `LIBDITTO_STATIC` is explicitly set to `1`, or unset.
If you have a special path to the `libdittoffi.a`/`dittoffi.lib` file (on Unix
and Windows, respectively), then you can use the `DITTOFFI_SEARCH_PATH` env var
to point to its location (using an absolute path), at linkage time (during
`cargo build` exclusively).
### Dynamically linking (advanced)
You can opt into this behavior by setting the `LIBDITTO_STATIC=0` environment
variable. When opting into this, you will have to handle library path
resolution to the `libdittoffi.so`/`libdittoffi.dylib`/`dittoffi.dll` file (on
Linux, macOS, and Windows, respectively).
That is, whilst the `DITTOFFI_SEARCH_PATH` is still important to help the
`cargo build` / linkage step resolve the dynamic library, the actual usage of
this file happens at _runtime_, when the (Rust) binary using `::dittolive_ditto`
is executed.
It is thus advisable to install the C dynamic library artifact under one of the
system folders, such as `/usr/lib` or whatnot on Unix.
Otherwise, you would have to either:
- meddle with link-time flags to set OS-specific loader metadata in the binary,
such as the `R{,UN}PATH` / `install_path`, paying special attention to the
choice of absolute paths, binary-relative paths (such as `$ORIGIN/...` on
Linux), or even working-directory-relative paths, or
- use env vars directives for the system dynamic loader, such as
`DYLD_FALLBACK_LIBRARY_PATH` on macOS, or `LD_LIBRARY_PATH` on Linux.
(For the technically-savvy, on macOS, the `install_path` of our `.dylib`
artifact is set to `$rpath/libdittoffi.dylib`).
</details>