1
//! Shows basic usage of a server.
2

            
3
use std::time::Duration;
4

            
5
use bonsaidb::client::url::Url;
6
use bonsaidb::client::AsyncClient;
7
use bonsaidb::core::connection::{AsyncConnection, AsyncStorageConnection};
8
use bonsaidb::core::schema::{SerializedCollection, SerializedView};
9
use bonsaidb::local::config::Builder;
10
use bonsaidb::server::{DefaultPermissions, Server, ServerConfiguration};
11
use rand::{thread_rng, Rng};
12

            
13
mod support;
14
use support::schema::{Shape, ShapesByNumberOfSides};
15

            
16
#[tokio::main]
17
1
async fn main() -> anyhow::Result<()> {
18
1
    env_logger::init();
19
    // ANCHOR: setup
20
1
    let server = Server::open(
21
1
        ServerConfiguration::new("server-data.bonsaidb")
22
1
            .default_permissions(DefaultPermissions::AllowAll)
23
1
            .with_schema::<Shape>()?,
24
    )
25
3
    .await?;
26
2
    if server.certificate_chain().await.is_err() {
27
10
        server.install_self_signed_certificate(true).await?;
28
    }
29
1
    let certificate = server
30
1
        .certificate_chain()
31
2
        .await?
32
1
        .into_end_entity_certificate();
33
2
    server.create_database::<Shape>("my-database", true).await?;
34
    // ANCHOR_END: setup
35

            
36
    // If websockets are enabled, we'll also listen for websocket traffic. The
37
    // QUIC-based connection should be overall better to use than WebSockets,
38
    // but it's much easier to route WebSocket traffic across the internet.
39
    #[cfg(feature = "websockets")]
40
1
    {
41
1
        let server = server.clone();
42
1
        tokio::spawn(async move {
43
1
            server
44
1
                .listen_for_websockets_on("localhost:8080", false)
45
2
                .await
46
1
        });
47
1
    }
48
1

            
49
1
    // Spawn our QUIC-based protocol listener.
50
1
    let task_server = server.clone();
51
7
    tokio::spawn(async move { task_server.listen_on(5645).await });
52
1

            
53
1
    // Give a moment for the listeners to start.
54
1
    tokio::time::sleep(Duration::from_millis(10)).await;
55

            
56
    // To allow this example to run both websockets and QUIC, we're going to gather the clients
57
    // into a collection and use join_all to wait until they finish.
58
1
    let mut tasks = Vec::new();
59
1
    #[cfg(feature = "websockets")]
60
1
    {
61
1
        // To connect over websockets, use the websocket scheme.
62
1
        tasks.push(do_some_database_work(
63
1
            AsyncClient::new(Url::parse("ws://localhost:8080")?)?
64
1
                .database::<Shape>("my-database")
65
                .await?,
66
1
            "websockets",
67
1
        ));
68
1
    }
69
1

            
70
1
    // To connect over QUIC, use the bonsaidb scheme.
71
1
    tasks.push(do_some_database_work(
72
1
        AsyncClient::build(Url::parse("bonsaidb://localhost")?)
73
1
            .with_certificate(certificate)
74
1
            .build()?
75
1
            .database::<Shape>("my-database")
76
            .await?,
77
1
        "bonsaidb",
78
1
    ));
79
1

            
80
1
    // Wait for the clients to finish
81
1
    futures::future::join_all(tasks)
82
102
        .await
83
1
        .into_iter()
84
1
        .collect::<Result<_, _>>()?;
85

            
86
    // Shut the server down gracefully (or forcefully after 5 seconds).
87
1
    server.shutdown(Some(Duration::from_secs(5))).await?;
88

            
89
1
    Ok(())
90
}
91

            
92
2
async fn do_some_database_work<'a, C: AsyncConnection>(
93
2
    database: C,
94
2
    client_name: &str,
95
2
) -> Result<(), bonsaidb::core::Error> {
96
    // Insert 50 random shapes
97
102
    for _ in 0u32..50 {
98
100
        let sides = {
99
100
            let mut rng = thread_rng();
100
100
            rng.gen_range(3..=10)
101
100
        };
102
191
        Shape::new(sides).push_into_async(&database).await?;
103
    }
104

            
105
    log::info!("Client {} finished", client_name);
106

            
107
    // Print a summary of all the shapes
108
16
    for result in ShapesByNumberOfSides::entries_async(&database)
109
2
        .reduce_grouped()
110
4
        .await?
111
    {
112
        log::info!(
113
            "Number of entries with {:02} sides: {}",
114
            result.key,
115
            result.value
116
        );
117
    }
118

            
119
2
    Ok(())
120
2
}
121

            
122
1
#[test]
123
1
fn runs() {
124
1
    main().unwrap()
125
1
}