rsfbclient/connection/builders/
builder_native.rs1use super::*;
2use crate::connection::{conn_string, TransactionConfiguration};
3use std::marker::PhantomData;
4
5#[doc(hidden)]
6pub use rsfbclient_native::{DynLink, DynLoad};
7
8use crate::transaction::{transaction_builder, TransactionConfigurationBuilder};
9use rsfbclient_native::{LinkageMarker, NativeFbAttachmentConfig, NativeFbClient, RemoteConfig};
10
11#[doc(hidden)]
13#[derive(Clone)]
14pub struct LinkageNotConfigured;
15#[doc(hidden)]
16#[derive(Clone)]
17pub struct ConnTypeNotConfigured;
18#[doc(hidden)]
19#[derive(Clone)]
20pub struct ConnEmbedded;
21#[doc(hidden)]
22#[derive(Clone)]
23pub struct ConnRemote;
24#[doc(hidden)]
25#[derive(Clone)]
26pub struct ConnByString;
27#[doc(hidden)]
28#[cfg(feature = "linking")]
29pub type DynByString = DynLink;
30#[cfg(not(feature = "linking"))]
31pub type DynByString = DynLoad;
32
33#[doc(hidden)]
38pub trait ConfiguredConnType: Send + Sync {}
39#[doc(hidden)]
40impl ConfiguredConnType for ConnEmbedded {}
41#[doc(hidden)]
42impl ConfiguredConnType for ConnRemote {}
43#[doc(hidden)]
44impl ConfiguredConnType for ConnByString {}
45
46#[doc(hidden)]
47pub trait ConfiguredLinkage {}
50#[doc(hidden)]
51impl ConfiguredLinkage for DynLink {}
52#[doc(hidden)]
53impl ConfiguredLinkage for DynLoad {}
54
55#[derive(Clone)]
64pub struct NativeConnectionBuilder<LinkageType, ConnectionType> {
65 _marker_linkage: PhantomData<LinkageType>,
66 _marker_conntype: PhantomData<ConnectionType>,
67 conn_conf: ConnectionConfiguration<NativeFbAttachmentConfig>,
68
69 charset: Charset,
70 lib_path: Option<String>,
71 page_size: Option<u32>,
72}
73
74impl<A, B> From<&NativeConnectionBuilder<A, B>>
75 for ConnectionConfiguration<NativeFbAttachmentConfig>
76{
77 fn from(arg: &NativeConnectionBuilder<A, B>) -> Self {
78 arg.conn_conf.clone()
79 }
80}
81
82pub fn builder_native() -> NativeConnectionBuilder<LinkageNotConfigured, ConnTypeNotConfigured> {
91 Default::default()
92}
93
94#[cfg(feature = "dynamic_loading")]
95impl<A> FirebirdClientFactory for NativeConnectionBuilder<DynLoad, A>
96where
97 A: ConfiguredConnType,
98{
99 type C = NativeFbClient<rsfbclient_native::DynLoad>;
101
102 fn new_instance(&self) -> Result<Self::C, FbError> {
103 let path = self
104 .lib_path
105 .as_ref()
106 .ok_or_else(|| FbError::from("The lib path is required to use the dynload loading"))?;
107
108 rsfbclient_native::DynLoad {
109 charset: self.charset.clone(),
110 lib_path: path.clone(),
111 }
112 .try_to_client()
113 }
114
115 fn get_conn_conf(&self) -> &ConnectionConfiguration<NativeFbAttachmentConfig> {
116 &self.conn_conf
117 }
118}
119
120#[cfg(feature = "linking")]
121impl<A> FirebirdClientFactory for NativeConnectionBuilder<DynLink, A>
122where
123 A: ConfiguredConnType,
124{
125 type C = NativeFbClient<rsfbclient_native::DynLink>;
127
128 fn new_instance(&self) -> Result<Self::C, FbError> {
129 Ok(rsfbclient_native::DynLink(self.charset.clone()).to_client())
130 }
131
132 fn get_conn_conf(&self) -> &ConnectionConfiguration<NativeFbAttachmentConfig> {
133 &self.conn_conf
134 }
135}
136
137impl<A, B> NativeConnectionBuilder<A, B>
138where
139 A: ConfiguredLinkage,
140 B: ConfiguredConnType,
141 A: LinkageMarker,
143 Self: FirebirdClientFactory<C = NativeFbClient<A>>,
144{
145 pub fn connect(&self) -> Result<Connection<NativeFbClient<A>>, FbError> {
147 Connection::open(self.new_instance()?, &self.conn_conf)
148 }
149
150 pub fn create_database(&self) -> Result<Connection<NativeFbClient<A>>, FbError> {
152 Connection::create_database(self.new_instance()?, &self.conn_conf, self.page_size)
153 }
154}
155
156impl<A, B> NativeConnectionBuilder<A, B>
157where
158 A: ConfiguredLinkage,
159 B: ConfiguredConnType,
160{
161 pub fn user<S: Into<String>>(&mut self, user: S) -> &mut Self {
163 self.conn_conf.attachment_conf.user = user.into();
164 self
165 }
166
167 pub fn db_name<S: Into<String>>(&mut self, db_name: S) -> &mut Self {
169 self.conn_conf.attachment_conf.db_name = db_name.into();
170 self
171 }
172
173 pub fn dialect(&mut self, dialect: Dialect) -> &mut Self {
175 self.conn_conf.dialect = dialect;
176 self
177 }
178
179 pub fn charset(&mut self, charset: Charset) -> &mut Self {
181 self.charset = charset;
182 self
183 }
184
185 pub fn stmt_cache_size(&mut self, stmt_cache_size: usize) -> &mut Self {
187 self.conn_conf.stmt_cache_size = stmt_cache_size;
188 self
189 }
190
191 pub fn page_size(&mut self, size: u32) -> &mut Self {
193 self.page_size = Some(size);
194 self
195 }
196
197 pub fn role<S: Into<String>>(&mut self, name: S) -> &mut Self {
199 self.conn_conf.attachment_conf.role_name = Some(name.into());
200 self
201 }
202
203 pub fn transaction(&mut self, conf: TransactionConfiguration) -> &mut Self {
205 self.conn_conf.transaction_conf = conf;
206 self
207 }
208
209 pub fn with_transaction<F>(&mut self, builder: F) -> &mut Self
211 where
212 F: FnOnce(&mut TransactionConfigurationBuilder) -> &mut TransactionConfigurationBuilder,
213 {
214 self.conn_conf.transaction_conf = builder(&mut transaction_builder()).build();
215 self
216 }
217
218 pub fn no_db_triggers(&mut self) -> &mut Self {
220 self.conn_conf.no_db_triggers = true;
221 self
222 }
223}
224
225impl<A, B> NativeConnectionBuilder<A, B> {
226 fn safe_transmute<X, Y>(self) -> NativeConnectionBuilder<X, Y> {
228 NativeConnectionBuilder {
229 _marker_linkage: PhantomData,
230 _marker_conntype: PhantomData,
231 conn_conf: self.conn_conf,
232 charset: self.charset,
233 lib_path: self.lib_path,
234 page_size: self.page_size,
235 }
236 }
237}
238
239impl Default for NativeConnectionBuilder<LinkageNotConfigured, ConnTypeNotConfigured> {
240 fn default() -> Self {
241 let mut self_result = Self {
242 _marker_linkage: PhantomData,
243 _marker_conntype: PhantomData,
244 conn_conf: Default::default(),
245 charset: charset::UTF_8,
246 lib_path: None,
247 page_size: None,
248 };
249
250 self_result.conn_conf.dialect = Dialect::D3;
251 self_result.conn_conf.stmt_cache_size = 20;
252 self_result.conn_conf.attachment_conf.remote = None;
253 self_result.conn_conf.attachment_conf.user = "SYSDBA".to_string();
254 self_result.conn_conf.attachment_conf.db_name = "test.fdb".to_string();
255 self_result.conn_conf.attachment_conf.role_name = None;
256
257 self_result
258 }
259}
260
261impl<A> NativeConnectionBuilder<A, ConnRemote> {
263 fn get_initialized_remote(&mut self) -> &mut RemoteConfig {
266 self.conn_conf
267 .attachment_conf
268 .remote
269 .get_or_insert(Default::default())
270 }
271
272 pub fn host<S: Into<String>>(&mut self, host: S) -> &mut Self {
274 self.get_initialized_remote().host = host.into();
275 self
276 }
277
278 pub fn port(&mut self, port: u16) -> &mut Self {
280 self.get_initialized_remote().port = port;
281 self
282 }
283
284 pub fn pass<S: Into<String>>(&mut self, pass: S) -> &mut Self {
286 self.get_initialized_remote().pass = pass.into();
287 self
288 }
289}
290
291impl<A> NativeConnectionBuilder<A, ConnTypeNotConfigured> {
292 pub fn with_remote(mut self) -> NativeConnectionBuilder<A, ConnRemote> {
295 let remote = rsfbclient_native::RemoteConfig {
296 host: "localhost".to_string(),
297 port: 3050,
298 pass: "masterkey".to_string(),
299 };
300 self.conn_conf.attachment_conf.remote = Some(remote);
301 self.safe_transmute()
302 }
303
304 pub fn with_embedded(self) -> NativeConnectionBuilder<A, ConnEmbedded> {
318 self.safe_transmute()
319 }
320}
321
322impl<A> NativeConnectionBuilder<LinkageNotConfigured, A> {
323 #[cfg(feature = "linking")]
329 pub fn with_dyn_link(self) -> NativeConnectionBuilder<DynLink, A> {
330 self.safe_transmute()
331 }
332
333 #[cfg(feature = "dynamic_loading")]
334 pub fn with_dyn_load<S: Into<String>>(
359 mut self,
360 lib_path: S,
361 ) -> NativeConnectionBuilder<DynLoad, A> {
362 self.lib_path = Some(lib_path.into());
363 self.safe_transmute()
364 }
365
366 #[allow(clippy::wrong_self_convention)]
376 pub fn from_string(
377 self,
378 s_conn: &str,
379 ) -> Result<NativeConnectionBuilder<DynByString, ConnByString>, FbError> {
380 let settings = conn_string::parse(s_conn)?;
381
382 let mut cb: NativeConnectionBuilder<DynByString, ConnRemote> = {
383 if let Some(host) = settings.host {
386 let mut cb = self.safe_transmute().with_remote();
387
388 cb.host(host);
389
390 if let Some(port) = settings.port {
391 cb.port(port);
392 }
393
394 if let Some(user) = settings.user {
395 cb.user(user);
396 }
397
398 if let Some(pass) = settings.pass {
399 cb.pass(pass);
400 }
401
402 cb
403 } else {
404 self.safe_transmute()
405 }
406 };
407
408 cb.db_name(settings.db_name);
409
410 if let Some(charset) = settings.charset {
411 cb.charset(charset);
412 }
413
414 if let Some(dialect) = settings.dialect {
415 cb.dialect(dialect);
416 }
417
418 if let Some(lib_path) = settings.lib_path {
419 cb.lib_path = Some(lib_path);
420 }
421
422 if let Some(stmt_cache_size) = settings.stmt_cache_size {
423 cb.stmt_cache_size(stmt_cache_size);
424 }
425
426 if let Some(role_name) = settings.role_name {
427 cb.role(role_name);
428 }
429
430 Ok(cb.safe_transmute())
431 }
432}