1use std::fmt;
4use std::sync::Arc;
5
6#[allow(unused_imports)]
7use crate::error::{Error, Result};
8use crate::schema::{Schema, SchemaPackage, Unbound};
9use type_bridge_orm::_registry::DescriptorRegistry;
10
11#[derive(Clone, PartialEq, Eq)]
13pub struct ConnectionOptions {
14 address: String,
15 database: String,
16 username: Option<String>,
17 password: Option<String>,
18 http_port: u16,
19 tls: bool,
20}
21
22impl fmt::Debug for ConnectionOptions {
23 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
24 formatter
25 .debug_struct("ConnectionOptions")
26 .field("address", &self.address)
27 .field("database", &self.database)
28 .field("username", &self.username)
29 .field("password", &self.password.as_ref().map(|_| "[REDACTED]"))
30 .field("http_port", &self.http_port)
31 .field("tls", &self.tls)
32 .finish()
33 }
34}
35
36impl ConnectionOptions {
37 #[must_use]
39 pub fn new(address: impl Into<String>, database: impl Into<String>) -> Self {
40 Self {
41 address: address.into(),
42 database: database.into(),
43 username: None,
44 password: None,
45 http_port: 8000,
46 tls: false,
47 }
48 }
49
50 #[must_use]
52 pub fn credentials(mut self, username: impl Into<String>, password: impl Into<String>) -> Self {
53 self.username = Some(username.into());
54 self.password = Some(password.into());
55 self
56 }
57
58 #[must_use]
60 pub fn http_port(mut self, port: u16) -> Self {
61 self.http_port = port;
62 self
63 }
64
65 #[must_use]
67 pub fn tls(mut self, enabled: bool) -> Self {
68 self.tls = enabled;
69 self
70 }
71
72 #[must_use]
74 pub fn address(&self) -> &str {
75 &self.address
76 }
77
78 #[must_use]
80 pub fn database(&self) -> &str {
81 &self.database
82 }
83
84 #[must_use]
86 pub fn get_http_port(&self) -> u16 {
87 self.http_port
88 }
89
90 #[must_use]
92 pub fn is_tls(&self) -> bool {
93 self.tls
94 }
95}
96
97impl From<(&str, &str)> for ConnectionOptions {
98 fn from((address, database): (&str, &str)) -> Self {
99 Self::new(address, database)
100 }
101}
102
103pub struct Database<S: Schema = Unbound> {
105 inner: type_bridge_orm::Database,
106 installed_schema: Option<Arc<type_bridge_orm::InstalledRuntimeProjection>>,
107 match_registry: Option<Arc<DescriptorRegistry>>,
108 marker: std::marker::PhantomData<fn() -> S>,
109}
110
111fn build_match_registry(
112 installed: &type_bridge_orm::InstalledRuntimeProjection,
113) -> Result<Arc<DescriptorRegistry>> {
114 installed
115 .match_registry()
116 .map(Arc::new)
117 .map_err(Error::from_orm)
118}
119
120impl Database<Unbound> {
121 #[cfg(feature = "typedb")]
123 pub async fn connect(options: impl Into<ConnectionOptions>) -> Result<Database<Unbound>> {
124 let opts = options.into();
125 let username = opts.username.as_deref().unwrap_or("admin");
126 let password = opts.password.as_deref().unwrap_or("password");
127 let orm_opts = type_bridge_orm::ConnectOptions {
128 http_port: opts.http_port,
129 tls: opts.tls,
130 ..type_bridge_orm::ConnectOptions::default()
131 };
132
133 let inner = type_bridge_orm::Database::connect_with_options(
134 &opts.address,
135 &opts.database,
136 username,
137 password,
138 orm_opts,
139 )
140 .await
141 .map_err(Error::from_orm)?;
142
143 Ok(Database {
144 inner,
145 installed_schema: None,
146 match_registry: None,
147 marker: std::marker::PhantomData,
148 })
149 }
150
151 #[allow(dead_code)]
153 pub(crate) fn from_orm_database(inner: type_bridge_orm::Database) -> Self {
154 Self {
155 inner,
156 installed_schema: None,
157 match_registry: None,
158 marker: std::marker::PhantomData,
159 }
160 }
161
162 pub fn with_schema<S: Schema>(self, schema: SchemaPackage<S>) -> Result<Database<S>> {
164 let installed = schema.verify_and_install()?;
165 let match_registry = build_match_registry(&installed)?;
166 Ok(Database {
167 inner: self.inner,
168 installed_schema: Some(installed),
169 match_registry: Some(match_registry),
170 marker: std::marker::PhantomData,
171 })
172 }
173}
174
175impl<S: Schema> Database<S> {
176 #[cfg(test)]
177 pub(crate) fn from_test_parts(
178 inner: type_bridge_orm::Database,
179 installed: type_bridge_orm::InstalledRuntimeProjection,
180 ) -> Self {
181 let installed = Arc::new(installed);
182 let match_registry =
183 build_match_registry(&installed).expect("test projection descriptors register");
184 Self {
185 inner,
186 installed_schema: Some(installed),
187 match_registry: Some(match_registry),
188 marker: std::marker::PhantomData,
189 }
190 }
191 #[cfg(test)]
192 pub(crate) fn from_test_unbound_parts(inner: type_bridge_orm::Database) -> Self {
193 Self {
194 inner,
195 installed_schema: None,
196 match_registry: None,
197 marker: std::marker::PhantomData,
198 }
199 }
200 pub fn entities<M>(&self) -> crate::entity_manager::EntityManager<'_, S, M>
202 where
203 M: crate::__codegen::EntityModel<Schema = S>,
204 {
205 crate::entity_manager::EntityManager::new(self)
206 }
207 pub fn relations<M>(&self) -> crate::relation_manager::RelationManager<'_, S, M>
209 where
210 M: crate::__codegen::RelationModel<Schema = S>,
211 {
212 crate::relation_manager::RelationManager::new(self)
213 }
214 pub async fn write(&self) -> Result<crate::transaction::WriteTransaction<'_, S>> {
219 crate::transaction::WriteTransaction::open(self).await
220 }
221 pub async fn read(&self) -> Result<crate::transaction::ReadTransaction<'_, S>> {
224 crate::transaction::ReadTransaction::open(self).await
225 }
226 #[must_use]
228 pub fn database_name(&self) -> &str {
229 self.inner.database_name()
230 }
231
232 #[must_use]
234 pub fn is_schema_bound(&self) -> bool {
235 self.installed_schema.is_some()
236 }
237
238 #[allow(dead_code)]
240 pub(crate) fn inner_orm(&self) -> &type_bridge_orm::Database {
241 &self.inner
242 }
243
244 #[allow(dead_code)]
246 pub(crate) fn installed_schema(
247 &self,
248 ) -> Option<&Arc<type_bridge_orm::InstalledRuntimeProjection>> {
249 self.installed_schema.as_ref()
250 }
251
252 #[allow(dead_code)]
254 pub(crate) fn match_registry(&self) -> Option<&Arc<DescriptorRegistry>> {
255 self.match_registry.as_ref()
256 }
257}