1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*!
EdgeDB client for Tokio

👉 New! Check out the new [EdgeDB client tutorial](`tutorial`). 👈

The main way to use EdgeDB bindings is to use the [`Client`]. It encompasses
connection pool to the database that is transparent for user. Individual
queries can be made via methods on the client. Correlated queries are done
via [transactions](Client::transaction).

To create a client, use the [`create_client`] function (it gets a database
connection configuration from environment). You can also use a [`Builder`]
to [`build`](`Builder::new`) custom [`Config`] and [create a
client](Client::new) using that config.

# Example

```rust,no_run
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let conn = edgedb_tokio::create_client().await?;
    let val = conn.query_required_single::<i64, _>(
        "SELECT 7*8",
        &(),
    ).await?;
    println!("7*8 is: {}", val);
    Ok(())
}
```
More [examples on github](https://github.com/edgedb/edgedb-rust/tree/master/edgedb-tokio/examples)

# Nice Error Reporting

We use [miette] crate for including snippets in your error reporting code.

To make it work, first you need enable `fancy` feature in your top-level
crate's `Cargo.toml`:
```toml
[dependencies]
miette = { version="5.3.0", features=["fancy"] }
edgedb-tokio = { version="*", features=["miette-errors"] }
```

Then if you use `miette` all the way through your application, it just
works:
```rust,no_run
#[tokio::main]
async fn main() -> miette::Result<()> {
    let conn = edgedb_tokio::create_client().await?;
    conn.query::<String, _>("SELECT 1+2)", &()).await?;
    Ok(())
}
```

However, if you use some boxed error container (e.g. [anyhow]), you
might need to downcast error for printing:
```rust,no_run
async fn do_something() -> anyhow::Result<()> {
    let conn = edgedb_tokio::create_client().await?;
    conn.query::<String, _>("SELECT 1+2)", &()).await?;
    Ok(())
}

#[tokio::main]
async fn main() {
    match do_something().await {
        Ok(res) => res,
        Err(e) => {
            e.downcast::<edgedb_tokio::Error>()
                .map(|e| eprintln!("{:?}", miette::Report::new(e)))
                .unwrap_or_else(|e| eprintln!("{:#}", e));
            std::process::exit(1);
        }
    }
}
```

In some cases, where parts of your code use `miette::Result` or
`miette::Report` before converting to the boxed (anyhow) container, you
might want a little bit more complex downcasting:

```rust,no_run
# async fn do_something() -> anyhow::Result<()> { unimplemented!() }
#[tokio::main]
async fn main() {
    match do_something().await {
        Ok(res) => res,
        Err(e) => {
            e.downcast::<edgedb_tokio::Error>()
                .map(|e| eprintln!("{:?}", miette::Report::new(e)))
                .or_else(|e| e.downcast::<miette::Report>()
                    .map(|e| eprintln!("{:?}", e)))
                .unwrap_or_else(|e| eprintln!("{:#}", e));
            std::process::exit(1);
        }
    }
}
```

Note that last two examples do hide error contexts from anyhow and do not
pretty print if `source()` of the error is `edgedb_errors::Error` but not
the top-level one. We leave those more complex cases as an excersize to the
reader.

[miette]: https://crates.io/crates/miette
[anyhow]: https://crates.io/crates/anyhow
*/

#![cfg_attr(
    not(feature = "unstable"),
    warn(missing_docs, missing_debug_implementations)
)]

#[cfg(feature = "unstable")]
pub mod credentials;
#[cfg(feature = "unstable")]
pub mod raw;
#[cfg(feature = "unstable")]
pub mod server_params;
#[cfg(feature = "unstable")]
pub mod tls;

#[cfg(not(feature = "unstable"))]
mod credentials;
#[cfg(not(feature = "unstable"))]
mod raw;
#[cfg(not(feature = "unstable"))]
mod server_params;
#[cfg(not(feature = "unstable"))]
mod tls;

mod builder;
mod client;
mod errors;
mod options;
mod sealed;
pub mod state;
mod transaction;
pub mod tutorial;

pub use edgedb_derive::{ConfigDelta, GlobalsDelta, Queryable};

pub use builder::{Builder, ClientSecurity, Config, InstanceName};
pub use client::Client;
pub use credentials::TlsSecurity;
pub use errors::Error;
pub use options::{RetryCondition, RetryOptions, TransactionOptions};
pub use state::{ConfigDelta, GlobalsDelta};
pub use transaction::Transaction;

#[cfg(feature = "unstable")]
pub use builder::get_project_dir;

/// Create a connection to the database with default parameters
///
/// It's expected that connection parameters are set up using environment
/// (either environment variables or project configuration in `edgedb.toml`)
/// so no configuration is specified here.
///
/// This method tries to esablish single connection immediately to
/// ensure that configuration is valid and will error out otherwise.
///
/// For more fine-grained setup see [`Client`] and [`Builder`] documentation
/// and the source of this function.
#[cfg(feature = "env")]
pub async fn create_client() -> Result<Client, Error> {
    let pool = Client::new(&Builder::new().build_env().await?);
    pool.ensure_connected().await?;
    Ok(pool)
}