Skip to main content

krabka_app_sdk/
client.rs

1//! Client construction and module accessors.
2
3use std::sync::{Arc, Mutex};
4
5use bytes::Bytes;
6
7use crate::{
8    connect_client::ConnectClient,
9    error::KrabkaError,
10    messaging::MessagingClient,
11    stubs::{AuthClient, BlobClient, DatabaseClient, QueuesClient},
12};
13
14/// Rust application SDK client.
15#[derive(Debug, Clone)]
16pub struct KrabkaClient {
17    pub(crate) inner: Arc<ClientInner>,
18}
19
20#[derive(Debug)]
21pub(crate) struct ClientInner {
22    pub(crate) endpoint: String,
23    pub(crate) bearer: Option<String>,
24    pub(crate) connect: ConnectClient,
25    pub(crate) mock: Mutex<MockState>,
26}
27
28/// In-memory backend used by `mock://` endpoints for conformance self-tests.
29#[derive(Debug, Default)]
30pub(crate) struct MockState {
31    pub(crate) messages: Vec<MockMessage>,
32    pub(crate) queue_sessions: Vec<MockQueueSession>,
33    pub(crate) queue_states: Vec<MockQueueMessageState>,
34}
35
36/// In-memory message used by the mock backend.
37#[derive(Debug, Clone)]
38pub(crate) struct MockMessage {
39    pub(crate) topic: String,
40    pub(crate) partition: i32,
41    pub(crate) offset: i64,
42    pub(crate) value: Bytes,
43    pub(crate) headers: Vec<(String, Option<Bytes>)>,
44}
45
46/// In-memory queue session used by the mock backend.
47#[derive(Debug, Clone, Default)]
48pub(crate) struct MockQueueSession {
49    pub(crate) id: String,
50    pub(crate) topic: String,
51    pub(crate) group: String,
52    pub(crate) max_messages: u32,
53}
54
55/// Message key delivered through a mock queue session.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub(crate) struct MockQueueDelivery {
58    pub(crate) topic: String,
59    pub(crate) partition: i32,
60    pub(crate) offset: i64,
61}
62
63/// Per-group queue state for one mock message.
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub(crate) struct MockQueueMessageState {
66    pub(crate) group: String,
67    pub(crate) delivery: MockQueueDelivery,
68    pub(crate) disposition: MockQueueDisposition,
69    pub(crate) delivery_count: i32,
70}
71
72/// Delivery disposition within one mock consumer group.
73#[derive(Debug, Clone, PartialEq, Eq)]
74pub(crate) enum MockQueueDisposition {
75    Available,
76    Acquired(String),
77    Accepted,
78    Rejected,
79}
80
81impl KrabkaClient {
82    /// Start building a client.
83    #[must_use]
84    pub fn builder() -> KrabkaClientBuilder {
85        KrabkaClientBuilder::default()
86    }
87
88    /// Create a client for an endpoint with default options.
89    ///
90    /// # Errors
91    ///
92    /// Returns [`KrabkaError::InvalidArgument`] when required client
93    /// configuration is missing.
94    pub fn new(endpoint: impl Into<String>) -> Result<Self, KrabkaError> {
95        Self::builder().endpoint(endpoint).build()
96    }
97
98    /// Access the messaging module.
99    #[must_use]
100    pub fn messaging(&self) -> MessagingClient {
101        MessagingClient::new(self.clone())
102    }
103
104    /// Access the queues module.
105    #[must_use]
106    pub fn queues(&self) -> QueuesClient {
107        QueuesClient::new(self.clone())
108    }
109
110    /// Access the database module.
111    #[must_use]
112    pub fn database(&self) -> DatabaseClient {
113        DatabaseClient::new()
114    }
115
116    /// Access the auth module.
117    #[must_use]
118    pub fn auth(&self) -> AuthClient {
119        AuthClient::new(self.inner.bearer.clone())
120    }
121
122    /// Access the blob module.
123    #[must_use]
124    pub fn blob(&self) -> BlobClient {
125        BlobClient::new()
126    }
127
128    pub(crate) fn is_mock(&self) -> bool {
129        self.inner.endpoint.starts_with("mock://")
130    }
131
132    pub(crate) fn is_unreachable(&self) -> bool {
133        self.inner.endpoint.starts_with("unreachable://")
134    }
135}
136
137/// Builder for [`KrabkaClient`].
138#[derive(Debug, Default)]
139pub struct KrabkaClientBuilder {
140    endpoint: Option<String>,
141    bearer: Option<String>,
142}
143
144impl KrabkaClientBuilder {
145    /// Set the gateway endpoint.
146    #[must_use]
147    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
148        self.endpoint = Some(endpoint.into());
149        self
150    }
151
152    /// Set a bearer token.
153    #[must_use]
154    pub fn bearer_token(mut self, bearer: impl Into<String>) -> Self {
155        self.bearer = Some(bearer.into());
156        self
157    }
158
159    /// Build the client.
160    ///
161    /// # Errors
162    ///
163    /// Returns [`KrabkaError::InvalidArgument`] when no endpoint was supplied.
164    pub fn build(self) -> Result<KrabkaClient, KrabkaError> {
165        let endpoint = self
166            .endpoint
167            .ok_or_else(|| KrabkaError::InvalidArgument("endpoint is required".into()))?;
168        Ok(KrabkaClient {
169            inner: Arc::new(ClientInner {
170                connect: ConnectClient::new(endpoint.clone(), self.bearer.clone()),
171                endpoint,
172                bearer: self.bearer,
173                mock: Mutex::new(MockState::default()),
174            }),
175        })
176    }
177}