1use std::{collections::BTreeMap, sync::Arc, time::Duration};
2
3use crate::{
4 client::{Client, ClientAuthStrategy},
5 error::SdkError,
6 providers::{ManagedSymmetricKeyProvider, ManagedSymmetricKeyProviderRegistry},
7};
8
9pub struct ClientBuilder {
10 base_url: String,
11 bearer_token: Option<String>,
12 tenant_id: Option<String>,
13 user_id: Option<String>,
14 timeout_secs: Option<u64>,
15 headers: BTreeMap<String, String>,
16 managed_symmetric_key_provider_registry: ManagedSymmetricKeyProviderRegistry,
17}
18
19impl ClientBuilder {
20 pub fn new(base_url: impl Into<String>) -> Self {
21 Self {
22 base_url: base_url.into(),
23 bearer_token: None,
24 tenant_id: None,
25 user_id: None,
26 timeout_secs: None,
27 headers: BTreeMap::new(),
28 managed_symmetric_key_provider_registry: ManagedSymmetricKeyProviderRegistry::new(),
29 }
30 }
31
32 pub fn with_bearer_token(mut self, bearer_token: impl Into<String>) -> Self {
33 self.bearer_token = Some(bearer_token.into());
34 self
35 }
36
37 pub fn with_tenant_id(mut self, tenant_id: impl Into<String>) -> Self {
38 self.tenant_id = Some(tenant_id.into());
39 self
40 }
41
42 pub fn with_user_id(mut self, user_id: impl Into<String>) -> Self {
43 self.user_id = Some(user_id.into());
44 self
45 }
46
47 pub fn with_timeout_secs(mut self, timeout_secs: u64) -> Self {
48 self.timeout_secs = Some(timeout_secs);
49 self
50 }
51
52 pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
53 self.headers.insert(name.into(), value.into());
54 self
55 }
56
57 pub fn with_managed_symmetric_key_provider<P>(mut self, provider: P) -> Self
58 where
59 P: ManagedSymmetricKeyProvider + 'static,
60 {
61 self.managed_symmetric_key_provider_registry
62 .register(provider);
63 self
64 }
65
66 pub fn with_managed_symmetric_key_provider_arc(
67 mut self,
68 provider: Arc<dyn ManagedSymmetricKeyProvider>,
69 ) -> Self {
70 self.managed_symmetric_key_provider_registry
71 .register_arc(provider);
72 self
73 }
74
75 pub fn build(self) -> Result<Client, SdkError> {
76 let base_url = self.base_url.trim().trim_end_matches('/').to_string();
77 if base_url.is_empty() {
78 return Err(SdkError::InvalidInput(
79 "base_url cannot be empty".to_string(),
80 ));
81 }
82
83 let mut headers = self.headers;
84 let tenant_id = self.tenant_id.map(|tenant_id| tenant_id.trim().to_string());
85 if let Some(tenant_id) = tenant_id.as_deref()
86 && !tenant_id.is_empty()
87 {
88 headers.insert("x-lattix-tenant-id".to_string(), tenant_id.to_string());
89 }
90 if let Some(user_id) = self.user_id {
91 let user_id = user_id.trim();
92 if !user_id.is_empty() {
93 headers.insert("x-lattix-user-id".to_string(), user_id.to_string());
94 }
95 }
96
97 let mut agent_builder = ureq::AgentBuilder::new();
98 if let Some(timeout_secs) = self.timeout_secs {
99 agent_builder = agent_builder.timeout(Duration::from_secs(timeout_secs));
100 }
101
102 let auth_strategy = if let Some(bearer_token) = self.bearer_token {
103 let bearer_token = bearer_token.trim().to_string();
104 if bearer_token.is_empty() {
105 None
106 } else {
107 Some(ClientAuthStrategy::StaticBearer(format!(
108 "Bearer {bearer_token}"
109 )))
110 }
111 } else {
112 None
113 };
114
115 Ok(Client::new(
116 base_url,
117 agent_builder.build(),
118 headers,
119 auth_strategy,
120 self.managed_symmetric_key_provider_registry,
121 ))
122 }
123}