bonsaidb_core/api.rs
1use std::fmt::{Debug, Display};
2use std::ops::Deref;
3
4pub use bonsaidb_macros::Api;
5use serde::{Deserialize, Serialize};
6
7use crate::schema::{Authority, Name, Qualified, QualifiedName};
8
9/// An API request type. This trait is used by BonsaiDb's server to allow a
10/// client to send a request of this type, and the server can respond with a
11/// `Result<`[`Api::Response`]`,`[`Api::Error`]`>`.
12///
13/// # Deriving this trait
14///
15/// This trait can be derived. The only required attribute is `name`:
16///
17/// - `name = "api-name"` or `name = "api-name", authority = "api-authority"`:
18/// Configures the Api's fully qualified name. This name must be unique across
19/// all other Apis installed. When creating an Api that is meant to be reused,
20/// ensure that a unique authority is used to prevent name collisions.
21/// - `response = ResponseType`: Configures the [`Api::Response`] associated
22/// type. This is the type that the handler will return upon success. If not
23/// specified, `()` is used.
24/// - `error = ErrorType`: Configures the [`Api::Error`] associated type. This
25/// is the type that the handler will return upon error. If not specified,
26/// [`Infallible`] is used.
27///
28/// ```rust
29/// use bonsaidb_core::api::Api;
30/// use serde::{Deserialize, Serialize};
31///
32/// #[derive(Api, Debug, Serialize, Deserialize)]
33/// #[api(name = "list-records", response = Vec<Record>)]
34/// # #[api(core = bonsaidb_core)]
35/// struct ListRecords {
36/// starting_id: u64,
37/// }
38///
39/// #[derive(Debug, Serialize, Deserialize, Clone)]
40/// struct Record {
41/// title: String,
42/// }
43/// ```
44pub trait Api: Serialize + for<'de> Deserialize<'de> + Send + Sync + Debug + 'static {
45 /// The type that represents an API response. This type will be sent to clients from the server.
46 type Response: Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + Debug;
47 /// The error type that this can return.
48 type Error: ApiError;
49
50 /// Returns the unique name of this api.
51 fn name() -> ApiName;
52}
53/// An Error type that can be used in within an [`Api`] definition.
54///
55/// The reason `std::convert::Infallible` can't be used is because `Api`
56/// errors must be able to be serialized across a network connection. While a
57/// value will never be present when this is Infallible, the associated type
58/// still must be declared as Serializable.
59#[derive(thiserror::Error, Debug, Clone, Serialize, Deserialize)]
60#[error("an unreachable error")]
61pub enum Infallible {}
62
63/// An error that can be used within a [`Api`] definition.
64pub trait ApiError:
65 std::fmt::Display + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + Debug
66{
67}
68
69impl<T> ApiError for T where
70 T: std::fmt::Display + Clone + Serialize + for<'de> Deserialize<'de> + Send + Sync + Debug
71{
72}
73
74/// The result of executing a custom API call.
75pub type ApiResult<Api> = Result<<Api as self::Api>::Response, <Api as self::Api>::Error>;
76
77/// The qualified name of an [`Api`](crate::api::Api).
78#[derive(Hash, PartialEq, Eq, Deserialize, Serialize, Debug, Clone, Ord, PartialOrd)]
79#[serde(transparent)]
80pub struct ApiName(QualifiedName);
81
82impl Qualified for ApiName {
83 fn new<A: Into<Authority>, N: Into<Name>>(authority: A, name: N) -> Self {
84 Self(QualifiedName::new(authority, name))
85 }
86}
87
88impl Display for ApiName {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 Display::fmt(&self.0, f)
91 }
92}
93
94impl Deref for ApiName {
95 type Target = QualifiedName;
96
97 fn deref(&self) -> &Self::Target {
98 &self.0
99 }
100}