1
use std::{fmt::Debug, marker::PhantomData};
2

            
3
use async_trait::async_trait;
4
use bonsaidb_core::{
5
    actionable,
6
    custom_api::{CustomApi, CustomApiError, Infallible},
7
    permissions::{Dispatcher, PermissionDenied},
8
    schema::{InsertError, InvalidNameError},
9
};
10

            
11
use crate::{server::ConnectedClient, CustomServer, Error};
12

            
13
/// Tailors the behavior of a server to your needs.
14
#[async_trait]
15
pub trait Backend: Debug + Send + Sync + Sized + 'static {
16
    /// The custom API definition. If you do not wish to have an API, `()` may be provided.
17
    type CustomApi: CustomApi;
18

            
19
    /// The type of data that can be stored in
20
    /// [`ConnectedClient::set_client_data`]. This allows state to be stored
21
    /// associated with each connected client.
22
    type ClientData: Send + Sync + Debug;
23

            
24
    /// The type that implements the
25
    /// [`Dispatcher`](bonsaidb_core::permissions::Dispatcher) trait.
26
    type CustomApiDispatcher: CustomApiDispatcher<Self>;
27

            
28
    /// Invoked once upon the server starting up.
29
    #[allow(unused_variables)]
30
74
    async fn initialize(server: &CustomServer<Self>) {}
31

            
32
    /// A client disconnected from the server. This is invoked before authentication has been performed.
33
    #[allow(unused_variables)]
34
    #[must_use]
35
116
    async fn client_connected(
36
116
        client: &ConnectedClient<Self>,
37
116
        server: &CustomServer<Self>,
38
116
    ) -> ConnectionHandling {
39
116
        log::info!(
40
            "{:?} client connected from {:?}",
41
            client.transport(),
42
            client.address()
43
        );
44

            
45
116
        ConnectionHandling::Accept
46
232
    }
47

            
48
    /// A client disconnected from the server.
49
    #[allow(unused_variables)]
50
57
    async fn client_disconnected(client: ConnectedClient<Self>, server: &CustomServer<Self>) {
51
57
        log::info!(
52
            "{:?} client disconnected ({:?})",
53
            client.transport(),
54
            client.address()
55
        );
56
57
    }
57

            
58
    /// A client successfully authenticated.
59
    #[allow(unused_variables)]
60
    async fn client_authenticated(client: ConnectedClient<Self>, server: &CustomServer<Self>) {
61
        log::info!(
62
            "{:?} client authenticated as user: {}",
63
            client.transport(),
64
            client.user_id().await.unwrap()
65
        );
66
    }
67
}
68

            
69
/// A trait that can dispatch requests for a [`CustomApi`].
70
pub trait CustomApiDispatcher<B: Backend>:
71
    Dispatcher<<B::CustomApi as CustomApi>::Request, Result = BackendApiResult<B::CustomApi>> + Debug
72
{
73
    /// Returns a dispatcher to handle custom api requests. The `server` and
74
    /// `client` parameters are provided to allow the dispatcher to have access
75
    /// to them when handling the individual actions.
76
    fn new(server: &CustomServer<B>, client: &ConnectedClient<B>) -> Self;
77
}
78

            
79
/// A [`Backend`] with no custom functionality.
80
#[cfg_attr(feature = "cli", derive(clap::Subcommand))]
81
#[derive(Debug)]
82
pub enum NoBackend {}
83

            
84
impl Backend for NoBackend {
85
    type CustomApi = ();
86
    type CustomApiDispatcher = NoDispatcher<Self>;
87
    type ClientData = ();
88
}
89

            
90
/// Defines a no-op dispatcher for a backend with no custom api.
91
#[derive(Debug)]
92
pub struct NoDispatcher<B: Backend>(PhantomData<B>);
93

            
94
impl<B: Backend<CustomApi = ()>> CustomApiDispatcher<B> for NoDispatcher<B> {
95
    fn new(_server: &CustomServer<B>, _client: &ConnectedClient<B>) -> Self {
96
        Self(PhantomData)
97
    }
98
}
99

            
100
#[async_trait]
101
impl<B: Backend<CustomApi = ()>> actionable::Dispatcher<()> for NoDispatcher<B> {
102
    type Result = Result<(), BackendError>;
103

            
104
    async fn dispatch(&self, _permissions: &actionable::Permissions, _request: ()) -> Self::Result {
105
        Ok(())
106
    }
107
}
108

            
109
/// Controls how a server should handle a connection.
110
pub enum ConnectionHandling {
111
    /// The server should accept this connection.
112
    Accept,
113
    /// The server should reject this connection.
114
    Reject,
115
}
116

            
117
/// An error that can occur inside of a [`Backend`] function.
118
#[derive(thiserror::Error, Debug)]
119
pub enum BackendError<E: CustomApiError = Infallible> {
120
    /// A backend-related error.
121
    #[error("backend error: {0}")]
122
    Backend(E),
123
    /// A server-related error.
124
    #[error("server error: {0}")]
125
    Server(#[from] Error),
126
}
127

            
128
impl<E: CustomApiError> From<PermissionDenied> for BackendError<E> {
129
4
    fn from(permission_denied: PermissionDenied) -> Self {
130
4
        Self::Server(Error::from(permission_denied))
131
4
    }
132
}
133

            
134
impl<E: CustomApiError> From<bonsaidb_core::Error> for BackendError<E> {
135
    fn from(err: bonsaidb_core::Error) -> Self {
136
        Self::Server(Error::from(err))
137
    }
138
}
139

            
140
impl<E: CustomApiError> From<InvalidNameError> for BackendError<E> {
141
    fn from(err: InvalidNameError) -> Self {
142
        Self::Server(Error::from(err))
143
    }
144
}
145

            
146
#[cfg(feature = "websockets")]
147
impl<E: CustomApiError> From<bincode::Error> for BackendError<E> {
148
    fn from(other: bincode::Error) -> Self {
149
        Self::Server(Error::from(bonsaidb_local::Error::from(other)))
150
    }
151
}
152

            
153
impl<E: CustomApiError> From<pot::Error> for BackendError<E> {
154
    fn from(other: pot::Error) -> Self {
155
        Self::Server(Error::from(bonsaidb_local::Error::from(other)))
156
    }
157
}
158

            
159
impl<T, E> From<InsertError<T>> for BackendError<E>
160
where
161
    E: CustomApiError,
162
{
163
    fn from(error: InsertError<T>) -> Self {
164
        Self::Server(Error::from(error.error))
165
    }
166
}
167

            
168
pub type BackendApiResult<Api> =
169
    Result<<Api as CustomApi>::Response, BackendError<<Api as CustomApi>::Error>>;