Skip to content

Commit 5f61397

Browse files
committed
fix(client): restructure client requests to use traits instead of enums for optimized stack usage
1 parent 336e352 commit 5f61397

File tree

2 files changed

+64
-49
lines changed

2 files changed

+64
-49
lines changed

src/client.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
2020
mod client_request;
2121

22-
use client_request::ClientRequest;
22+
use client_request::{ClientRequest, PingRequest, VerifyApiTokenRequest};
2323

2424
use reqwest;
2525
use url::Url;
2626

27-
use crate::{error::ClientError, event::ManagementEvent};
27+
use crate::error::ClientError;
2828

2929
/// Client for an [EventsourcingDB](https://www.eventsourcingdb.io/) instance.
3030
#[derive(Debug)]
@@ -74,7 +74,7 @@ impl Client {
7474
///
7575
/// # Errors
7676
/// This function will return an error if the request fails or if the URL is invalid.
77-
async fn request(&self, endpoint: ClientRequest) -> Result<reqwest::Response, ClientError> {
77+
async fn request<R: ClientRequest>(&self, endpoint: R) -> Result<R::Response, ClientError> {
7878
let url = self
7979
.base_url
8080
.join(endpoint.url_path())
@@ -86,7 +86,7 @@ impl Client {
8686
_ => return Err(ClientError::InvalidRequestMethod),
8787
}
8888
.header("Authorization", format!("Bearer {}", self.api_token));
89-
let request = if let Some(body) = endpoint.json() {
89+
let request = if let Some(body) = endpoint.body() {
9090
request
9191
.header("Content-Type", "application/json")
9292
.json(&body?)
@@ -97,7 +97,9 @@ impl Client {
9797
let response = request.send().await?;
9898

9999
if response.status().is_success() {
100-
Ok(response)
100+
let result = response.json().await?;
101+
endpoint.validate_response(&result)?;
102+
Ok(result)
101103
} else {
102104
Err(ClientError::DBError(
103105
response.status(),
@@ -107,7 +109,7 @@ impl Client {
107109
}
108110

109111
/// Pings the DB instance to check if it is reachable.
110-
///
112+
///
111113
/// ```
112114
/// # tokio_test::block_on(async {
113115
/// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
@@ -123,17 +125,12 @@ impl Client {
123125
/// # Errors
124126
/// This function will return an error if the request fails or if the URL is invalid.
125127
pub async fn ping(&self) -> Result<(), ClientError> {
126-
let response = self.request(ClientRequest::Ping).await?;
127-
if response.json::<ManagementEvent>().await?.ty() == "io.eventsourcingdb.api.ping-received"
128-
{
129-
Ok(())
130-
} else {
131-
Err(ClientError::PingFailed)
132-
}
128+
let _ = self.request(PingRequest).await?;
129+
Ok(())
133130
}
134131

135132
/// Verifies the API token by sending a request to the DB instance.
136-
///
133+
///
137134
/// ```
138135
/// # tokio_test::block_on(async {
139136
/// # let container = eventsourcingdb_client_rust::container::Container::start_default().await.unwrap();
@@ -149,13 +146,7 @@ impl Client {
149146
/// # Errors
150147
/// This function will return an error if the request fails or if the URL is invalid.
151148
pub async fn verify_api_token(&self) -> Result<(), ClientError> {
152-
let response = self.request(ClientRequest::VerifyApiToken).await?;
153-
if response.json::<ManagementEvent>().await?.ty()
154-
== "io.eventsourcingdb.api.api-token-verified"
155-
{
156-
Ok(())
157-
} else {
158-
Err(ClientError::APITokenInvalid)
159-
}
149+
let _ = self.request(VerifyApiTokenRequest).await?;
150+
Ok(())
160151
}
161152
}

src/client/client_request.rs

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,65 @@
1+
//! This is a purely internal module to represent client requests to the database.
2+
13
use reqwest::Method;
24
use serde_json::Value;
35

4-
use crate::error::ClientError;
6+
use crate::{error::ClientError, event::ManagementEvent};
57

6-
/// Enum for different requests that can be made to the DB
7-
#[derive(Debug)]
8-
pub enum ClientRequest {
9-
/// Ping the DB instance to check if it is reachable
10-
Ping,
11-
/// Verify the API token by sending a request to the DB instance
12-
VerifyApiToken,
13-
}
8+
/// Represents a request to the database client
9+
pub trait ClientRequest {
10+
const URL_PATH: &'static str;
11+
const METHOD: Method;
12+
type Response: serde::de::DeserializeOwned;
1413

15-
impl ClientRequest {
1614
/// Returns the URL path for the request
17-
#[must_use]
18-
pub fn url_path(&self) -> &'static str {
19-
match self {
20-
ClientRequest::Ping => "/api/v1/ping",
21-
ClientRequest::VerifyApiToken => "/api/v1/verify-api-token",
22-
}
15+
fn url_path(&self) -> &'static str {
16+
Self::URL_PATH
2317
}
2418

2519
/// Returns the http method type for the request
26-
#[must_use]
27-
pub fn method(&self) -> Method {
28-
match self {
29-
ClientRequest::Ping => Method::GET,
30-
ClientRequest::VerifyApiToken => Method::POST,
31-
}
20+
fn method(&self) -> Method {
21+
Self::METHOD
3222
}
3323

3424
/// Returns the body for the request
35-
#[must_use]
36-
pub fn json(self) -> Option<Result<Value, ClientError>> {
37-
match self {
38-
ClientRequest::Ping | ClientRequest::VerifyApiToken => None,
39-
}
25+
fn body(&self) -> Option<Result<Value, ClientError>> {
26+
None
27+
}
28+
29+
/// Validate the response from the database
30+
fn validate_response(&self, _response: &Self::Response) -> Result<(), ClientError> {
31+
Ok(())
32+
}
33+
}
34+
35+
/// Ping the Database instance
36+
#[derive(Debug, Clone, Copy)]
37+
pub struct PingRequest;
38+
39+
impl ClientRequest for PingRequest {
40+
const URL_PATH: &'static str = "/api/v1/ping";
41+
const METHOD: Method = Method::GET;
42+
type Response = ManagementEvent;
43+
44+
fn validate_response(&self, response: &Self::Response) -> Result<(), ClientError> {
45+
(response.ty() == "io.eventsourcingdb.api.ping-received")
46+
.then_some(())
47+
.ok_or(ClientError::PingFailed)
48+
}
49+
}
50+
51+
/// Verify the API token
52+
#[derive(Debug, Clone, Copy)]
53+
pub struct VerifyApiTokenRequest;
54+
55+
impl ClientRequest for VerifyApiTokenRequest {
56+
const URL_PATH: &'static str = "/api/v1/verify-api-token";
57+
const METHOD: Method = Method::POST;
58+
type Response = ManagementEvent;
59+
60+
fn validate_response(&self, response: &Self::Response) -> Result<(), ClientError> {
61+
(response.ty() == "io.eventsourcingdb.api.api-token-verified")
62+
.then_some(())
63+
.ok_or(ClientError::APITokenInvalid)
4064
}
4165
}

0 commit comments

Comments
 (0)