Skip to main content

cratestack_client_rust/client/
core.rs

1use std::sync::Arc;
2
3use cratestack_codec_cbor::CborCodec;
4
5use crate::auth::RequestAuthorizer;
6use crate::codec::HttpClientCodec;
7use crate::config::ClientConfig;
8use crate::error::ClientError;
9use crate::state::{ClientStateStore, InMemoryStateStore, PersistedClientState};
10
11#[derive(Clone)]
12pub struct CratestackClient<C = CborCodec> {
13    pub(crate) http: reqwest::Client,
14    pub(crate) config: ClientConfig,
15    pub(crate) codec: C,
16    pub(crate) state_store: Arc<dyn ClientStateStore>,
17    pub(crate) request_authorizer: Option<Arc<dyn RequestAuthorizer>>,
18    /// The generating schema's `SCHEMA_SHA256` (issue #178) — `None` for a
19    /// hand-constructed client that isn't wrapped by generated `Client`
20    /// code (e.g. a bare `CratestackClient` in a test). Set automatically
21    /// by the schema-generated `Client::new` wrapper, never by the schema
22    /// author directly. Sent as `x-cratestack-schema-sha` on every request
23    /// when present; the server-side counterpart only ever warns on
24    /// mismatch, never rejects.
25    pub(crate) schema_sha: Option<&'static str>,
26}
27
28impl CratestackClient<CborCodec> {
29    pub fn cbor(config: ClientConfig) -> Self {
30        Self::new(config, CborCodec)
31    }
32}
33
34impl<C> CratestackClient<C>
35where
36    C: HttpClientCodec,
37{
38    pub fn new(config: ClientConfig, codec: C) -> Self {
39        Self {
40            http: reqwest::Client::new(),
41            config,
42            codec,
43            state_store: Arc::new(InMemoryStateStore::default()),
44            request_authorizer: None,
45            schema_sha: None,
46        }
47    }
48
49    pub fn with_http_client(config: ClientConfig, codec: C, http: reqwest::Client) -> Self {
50        Self {
51            http,
52            config,
53            codec,
54            state_store: Arc::new(InMemoryStateStore::default()),
55            request_authorizer: None,
56            schema_sha: None,
57        }
58    }
59
60    pub fn with_state_store(mut self, state_store: Arc<dyn ClientStateStore>) -> Self {
61        self.state_store = state_store;
62        self
63    }
64
65    pub fn with_optional_state_store(self, state_store: Option<Arc<dyn ClientStateStore>>) -> Self {
66        match state_store {
67            Some(state_store) => self.with_state_store(state_store),
68            None => self,
69        }
70    }
71
72    pub fn with_request_authorizer(
73        mut self,
74        request_authorizer: Arc<dyn RequestAuthorizer>,
75    ) -> Self {
76        self.request_authorizer = Some(request_authorizer);
77        self
78    }
79
80    /// Stamps the generating schema's `SCHEMA_SHA256` onto this client, so
81    /// every subsequent request carries `x-cratestack-schema-sha` (issue
82    /// #178). Called by the schema-generated `Client::new` wrapper, not
83    /// meant to be called directly by schema authors — public because the
84    /// generated code lives in a downstream crate.
85    pub fn with_schema_sha(mut self, schema_sha: &'static str) -> Self {
86        self.schema_sha = Some(schema_sha);
87        self
88    }
89
90    pub fn state(&self) -> Result<PersistedClientState, ClientError> {
91        self.state_store.load()
92    }
93}