1
use bonsaidb_core::api::Infallible;
2
use bonsaidb_core::arc_bytes::serde::Bytes;
3
use bonsaidb_core::networking;
4
use bonsaidb_core::schema::Name;
5

            
6
/// Errors related to working with the BonsaiDb client.
7
1320
#[derive(thiserror::Error, Debug)]
8
pub enum Error {
9
    #[cfg(feature = "websockets")]
10
    /// An error occurred from the WebSocket transport layer.
11
    #[error("a transport error occurred: '{0}'")]
12
    WebSocket(crate::client::WebSocketError),
13

            
14
    /// An error occurred from networking.
15
    #[error("a networking error occurred: '{0}'")]
16
    Network(#[from] bonsaidb_core::networking::Error),
17

            
18
    /// An invalid Url was provided.
19
    #[error("invalid url: '{0}'")]
20
    InvalidUrl(String),
21

            
22
    /// A BonsaiDb error occurred.
23
    #[error("{0}")]
24
    Core(#[from] bonsaidb_core::Error),
25

            
26
    /// An error from a `Api`. The actual error is still serialized, as it
27
    /// could be any type.
28
    #[error("api {name} error")]
29
    Api {
30
        /// The unique name of the api that responded with an error
31
        name: Name,
32
        /// The serialized bytes of the error type.
33
        error: Bytes,
34
    },
35

            
36
    /// The server is incompatible with this version of the client.
37
    #[error("server incompatible with client protocol version")]
38
    ProtocolVersionMismatch,
39
}
40

            
41
impl Error {
42
3120
    pub(crate) fn disconnected() -> Self {
43
3120
        Self::Core(bonsaidb_core::Error::Networking(
44
3120
            networking::Error::Disconnected,
45
3120
        ))
46
3120
    }
47

            
48
120
    pub(crate) fn request_timeout() -> Self {
49
120
        Self::Core(bonsaidb_core::Error::Networking(
50
120
            networking::Error::RequestTimeout,
51
120
        ))
52
120
    }
53

            
54
120
    pub(crate) fn connect_timeout() -> Self {
55
120
        Self::Core(bonsaidb_core::Error::Networking(
56
120
            networking::Error::ConnectTimeout,
57
120
        ))
58
120
    }
59
}
60

            
61
impl<T> From<flume::SendError<T>> for Error {
62
    fn from(_: flume::SendError<T>) -> Self {
63
        Self::disconnected()
64
    }
65
}
66

            
67
impl From<flume::RecvTimeoutError> for Error {
68
60
    fn from(err: flume::RecvTimeoutError) -> Self {
69
60
        match err {
70
60
            flume::RecvTimeoutError::Timeout => Self::request_timeout(),
71
            flume::RecvTimeoutError::Disconnected => Self::disconnected(),
72
        }
73
60
    }
74
}
75

            
76
impl From<flume::RecvError> for Error {
77
    fn from(_: flume::RecvError) -> Self {
78
        Self::disconnected()
79
    }
80
}
81

            
82
impl From<Error> for bonsaidb_core::Error {
83
17010
    fn from(other: Error) -> Self {
84
17010
        match other {
85
16920
            Error::Core(err) => err,
86
90
            other => Self::other("bonsaidb-client", other),
87
        }
88
17010
    }
89
}
90

            
91
#[cfg(feature = "websockets")]
92
impl From<bincode::Error> for Error {
93
    fn from(other: bincode::Error) -> Self {
94
        Self::Core(bonsaidb_core::Error::other("bincode", other))
95
    }
96
}
97

            
98
#[cfg(not(target_arch = "wasm32"))]
99
mod fabruic_impls {
100
    macro_rules! impl_from_fabruic {
101
        ($error:ty) => {
102
            impl From<$error> for $crate::Error {
103
480
                fn from(other: $error) -> Self {
104
480
                    Self::Core(bonsaidb_core::Error::other("quic", other))
105
480
                }
106
            }
107
        };
108
    }
109

            
110
    impl_from_fabruic!(fabruic::error::Sender);
111
    impl_from_fabruic!(fabruic::error::Receiver);
112
    impl_from_fabruic!(fabruic::error::Stream);
113
    impl_from_fabruic!(fabruic::error::Connecting);
114
    impl_from_fabruic!(fabruic::error::Connect);
115
}
116

            
117
#[cfg(feature = "websockets")]
118
impl From<crate::client::WebSocketError> for Error {
119
    #[cfg(not(target_arch = "wasm32"))]
120
60
    fn from(err: crate::client::WebSocketError) -> Self {
121
60
        if let crate::client::WebSocketError::Http(response) = &err {
122
30
            if response.status() == 406 {
123
30
                return Self::ProtocolVersionMismatch;
124
            }
125
30
        }
126

            
127
30
        Self::WebSocket(err)
128
60
    }
129

            
130
    #[cfg(target_arch = "wasm32")]
131
    fn from(err: crate::client::WebSocketError) -> Self {
132
        Self::WebSocket(err)
133
    }
134
}
135

            
136
impl From<pot::Error> for Error {
137
    fn from(err: pot::Error) -> Self {
138
        Self::from(bonsaidb_core::Error::from(err))
139
    }
140
}
141

            
142
/// An error returned from an api request.
143
#[derive(thiserror::Error, Debug)]
144
pub enum ApiError<T> {
145
    /// The API returned its own error type.
146
    #[error("api error: {0}")]
147
    Api(T),
148
    /// An error from BonsaiDb occurred.
149
    #[error("client error: {0}")]
150
    Client(#[from] Error),
151
}
152

            
153
impl From<ApiError<Self>> for bonsaidb_core::Error {
154
17010
    fn from(error: ApiError<Self>) -> Self {
155
17010
        match error {
156
            ApiError::Api(err) => err,
157
17010
            ApiError::Client(err) => Self::from(err),
158
        }
159
17010
    }
160
}
161

            
162
impl From<ApiError<Infallible>> for Error {
163
    fn from(err: ApiError<Infallible>) -> Self {
164
        match err {
165
            ApiError::Client(err) => err,
166
            ApiError::Api(_) => unreachable!("infallible"),
167
        }
168
    }
169
}