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

            
3
use std::time::Duration;
4

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

            
16
mod support;
17
use support::schema::{Shape, ShapesByNumberOfSides};
18

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

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

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

            
56
1
    // Give a moment for the listeners to start.
57
1
    tokio::time::sleep(Duration::from_millis(10)).await;
58

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

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

            
83
1
    // Wait for the clients to finish
84
137
    futures::future::join_all(tasks)
85
137
        .await
86
1
        .into_iter()
87
1
        .collect::<Result<_, _>>()?;
88

            
89
    // Shut the server down gracefully (or forcefully after 5 seconds).
90
1
    server.shutdown(Some(Duration::from_secs(5))).await?;
91

            
92
1
    Ok(())
93
1
}
94

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

            
108
    log::info!("Client {} finished", client_name);
109

            
110
    // Print a summary of all the shapes
111
16
    for result in database
112
2
        .view::<ShapesByNumberOfSides>()
113
3
        .reduce_grouped()
114
3
        .await?
115
    {
116
        log::info!(
117
            "Number of entries with {:02} sides: {}",
118
            result.key,
119
            result.value
120
        );
121
    }
122

            
123
2
    Ok(())
124
2
}
125

            
126
1
#[test]
127
1
fn runs() {
128
1
    main().unwrap()
129
1
}