sdecay/database.rs
1//! Defines safe outer database types
2//!
3//! Unsafe: no
4
5use core::{fmt::Debug, ops::Deref, pin::Pin};
6
7use crate::{
8 as_cpp_string::AsCppString,
9 container::{Container, RefContainer},
10 wrapper::{CppException, SandiaDecayDataBase},
11};
12
13/// `SandiaDecay`'s database with no info actually stored. Technically, it's already initialized, but I assume none of the calls would return meaningful info (so none are exposed)
14///
15/// To be used in any meaningful way, you need to obtain [`GenericDatabase`] using one of the following methods:
16/// - [`GenericUninitDatabase::init`]
17/// - [`GenericUninitDatabase::init_bytes`]
18/// - [`GenericUninitDatabase::init_env`]
19///
20/// See respective docs for details
21pub struct GenericUninitDatabase<C: Container<Inner = SandiaDecayDataBase>>(C);
22
23/// Not initialized database stored in the [`alloc::boxed::Box`]
24///
25/// For more details, see [`GenericUninitDatabase`]
26#[cfg(feature = "alloc")]
27pub type UninitDatabase =
28 GenericUninitDatabase<crate::container::BoxContainer<SandiaDecayDataBase>>;
29/// Not initialized database stored in the [`std::sync::Arc`]
30///
31/// For more details, see [`GenericUninitDatabase`]
32#[cfg(feature = "alloc")]
33pub type UninitSharedDatabase =
34 GenericUninitDatabase<crate::container::ArcContainer<SandiaDecayDataBase>>;
35/// Not initialized database stored in wherever the `&`[`core::mem::MaybeUninit`] pointed to
36///
37/// For more details, see [`GenericUninitDatabase`]
38pub type UninitLocalDatabase<'l> = GenericUninitDatabase<RefContainer<'l, SandiaDecayDataBase>>;
39
40impl<C: Container<Inner = SandiaDecayDataBase>> Debug for GenericUninitDatabase<C> {
41 #[inline]
42 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
43 f.write_str("UninintDatabase")
44 }
45}
46
47impl<C: Container<Inner = SandiaDecayDataBase>> Default for GenericUninitDatabase<C>
48where
49 C::Allocator: Default,
50{
51 #[inline]
52 fn default() -> Self {
53 Self::new()
54 }
55}
56
57impl<C: Container<Inner = SandiaDecayDataBase>> GenericUninitDatabase<C> {
58 /// Allocates empty database
59 #[inline]
60 pub fn new_in(allocator: C::Allocator) -> Self {
61 Self(SandiaDecayDataBase::new(allocator))
62 }
63
64 /// Same as [`GenericUninitDatabase::new_in`], but allocator is created via [`Default::default`]
65 #[inline]
66 pub fn new() -> Self
67 where
68 C::Allocator: Default,
69 {
70 Self(SandiaDecayDataBase::new(C::Allocator::default()))
71 }
72
73 fn get_mut(&mut self) -> Pin<&mut SandiaDecayDataBase> {
74 self.0
75 .try_inner()
76 .expect("Uninit database should not be in a shared container")
77 }
78
79 /// Attempts to initialize the database via path to the database `.xml` file
80 ///
81 /// ### Returns
82 /// - [`Result::Ok`] indicates successfully initialized database
83 /// - [`Result::Err`] indicates a failure to initialize a database. Actually returned value is a tuple of uninitialized database and exception thrown on C++ side
84 ///
85 /// ### Example
86 /// An example using [`crate::container::BoxContainer`] for storage:
87 /// ```rust,no_run
88 /// # #[cfg(feature = "alloc")] {
89 /// # use sdecay::database::UninitDatabase;
90 /// // assuming `database.xml` contains database data
91 /// let database = UninitDatabase::new()
92 /// .init("database.xml")
93 /// .expect("`database.xml` should exist and contain a valid database data");
94 /// # }
95 /// ```
96 ///
97 /// Note, that path can be any [`AsCppString`] implementor - see it's doc to find the most convenient for you
98 pub fn init(
99 mut self,
100 path: impl AsCppString,
101 ) -> Result<GenericDatabase<C>, (GenericUninitDatabase<C>, CppException)> {
102 match self.get_mut().init_path(path) {
103 Ok(()) => Ok(GenericDatabase(self.0)),
104 Err(exception) => Err((self, exception)),
105 }
106 }
107
108 /// Attempts to initialize the database via `xml` data
109 ///
110 /// ### Returns
111 /// - [`Result::Ok`] indicates successfully initialized database
112 /// - [`Result::Err`] indicates a failure to initialize a database. Actually returned value is a tuple of uninitialized database and exception thrown on C++ side
113 ///
114 /// ### Example
115 /// An example using [`crate::container::BoxContainer`] for storage:
116 /// ```rust,no_run
117 /// # #[cfg(feature = "alloc")] {
118 /// let data: &[u8] = br#"<?xml version="1.0"?><document>...</document>"#; // assuming `data contains valid database data`
119 /// # use sdecay::database::UninitDatabase;
120 /// let database = UninitDatabase::new()
121 /// .init_bytes(data)
122 /// .expect("Should provide valid database data");
123 /// # }
124 /// ```
125 pub fn init_bytes(
126 mut self,
127 bytes: impl AsRef<[u8]>,
128 ) -> Result<GenericDatabase<C>, (GenericUninitDatabase<C>, CppException)> {
129 match self.get_mut().init_bytes(bytes) {
130 Ok(()) => Ok(GenericDatabase(self.0)),
131 Err(exception) => Err((self, exception)),
132 }
133 }
134}
135
136/// Error while initializing database by path from environment variable
137///
138/// Returned by [`GenericUninitDatabase::init_env`] and [`GenericDatabase::from_env`]
139#[derive(Debug, Error)]
140pub enum EnvInitError {
141 /// `SANDIA_DATABASE_PATH` is not present in the environment
142 #[error("No `SANDIA_DATABASE_PATH` variable in the environment")]
143 NoEnvVar,
144 /// Envvar present, but exception thrown from C++ side
145 #[error(transparent)]
146 Exception(CppException),
147}
148
149impl<C: Container<Inner = SandiaDecayDataBase>> GenericUninitDatabase<C> {
150 /// Attempts to initialize database by a path from `SANDIA_DATABASE_PATH` environment variable
151 /// ### Returns
152 /// - [`Result::Ok`] indicates successfully initialized database
153 /// - [`Result::Err`] indicates a failure to initialize a database. Actually returned value is a tuple of uninitialized database and [`EnvInitError`]
154 ///
155 /// ### Example
156 /// An example using [`crate::container::BoxContainer`] for storage:
157 /// ```rust,no_run
158 /// // assuming `SANDIA_DATABASE_PATH` envvar contains path to database file
159 /// # use sdecay::database::UninitDatabase;
160 /// let database = UninitDatabase::new()
161 /// .init_env()
162 /// .expect("`SANDIA_DATABASE_PATH` should contain path to data, and data should be valid");
163 /// ```
164 #[cfg(feature = "std")]
165 #[inline]
166 pub fn init_env(self) -> Result<GenericDatabase<C>, (GenericUninitDatabase<C>, EnvInitError)> {
167 let Some(env_path) = std::env::var_os("SANDIA_DATABASE_PATH") else {
168 return Err((self, EnvInitError::NoEnvVar));
169 };
170 self.init(env_path)
171 .map_err(|(uninit, exception)| (uninit, EnvInitError::Exception(exception)))
172 }
173}
174
175impl<C: Container<Inner = SandiaDecayDataBase>> GenericUninitDatabase<C> {
176 /// Creates initialized database from embedded "default" database
177 ///
178 /// ### Example
179 /// Using [`crate::container::BoxContainer`] for storage:
180 /// ```rust
181 /// # #[cfg(feature = "alloc")] {
182 /// # use sdecay::database::UninitDatabase;
183 /// let database = UninitDatabase::new().init_vendor();
184 /// # }
185 /// ```
186 #[cfg(feature = "database")]
187 #[inline]
188 pub fn init_vendor(self) -> GenericDatabase<C> {
189 self.init_bytes(sdecay_sys::database::DATABASE)
190 .expect("Embedded database should be valid")
191 }
192
193 /// Creates initialized database from embedded "min" database
194 ///
195 /// ### Example
196 /// Using [`crate::container::BoxContainer`] for storage:
197 /// ```rust
198 /// # #[cfg(feature = "alloc")] {
199 /// # use sdecay::database::UninitDatabase;
200 /// let database = UninitDatabase::new().init_vendor_min();
201 /// # }
202 /// ```
203 #[cfg(feature = "database-min")]
204 #[inline]
205 pub fn init_vendor_min(self) -> GenericDatabase<C> {
206 self.init_bytes(sdecay_sys::database::DATABASE_MIN)
207 .expect("Embedded database should be valid")
208 }
209
210 /// Creates initialized database from embedded "nocoinc-min" database
211 ///
212 /// ### Example
213 /// Using [`crate::container::BoxContainer`] for storage:
214 /// ```rust
215 /// # #[cfg(feature = "alloc")] {
216 /// # use sdecay::database::UninitDatabase;
217 /// let database = UninitDatabase::new().init_vendor_nocoinc_min();
218 /// # }
219 /// ```
220 #[cfg(feature = "database-nocoinc-min")]
221 #[inline]
222 pub fn init_vendor_nocoinc_min(self) -> GenericDatabase<C> {
223 self.init_bytes(sdecay_sys::database::DATABASE_NOCOINC_MIN)
224 .expect("Embedded database should be valid")
225 }
226}
227
228/// Initialized and data-enabled `SandiaDecay` database. Can be created from [`GenericUninitDatabase`] (see it's doc), or directly via
229/// - [`GenericDatabase::from_path`] ([`GenericDatabase::from_path_in`])
230/// - [`GenericDatabase::from_bytes`] ([`GenericDatabase::from_bytes_in`])
231/// - [`GenericDatabase::from_env`] ([`GenericDatabase::from_env_in`])
232///
233/// See functions below for usage examples
234#[derive(Debug, Clone)]
235pub struct GenericDatabase<C: Container<Inner = SandiaDecayDataBase>>(C);
236
237/// Initialized database stored in the [`alloc::boxed::Box`]
238///
239/// For more details, see [`GenericDatabase`]
240#[cfg(feature = "alloc")]
241pub type Database = GenericDatabase<crate::container::BoxContainer<SandiaDecayDataBase>>;
242/// Initialized database stored in the [`std::sync::Arc`]
243///
244/// For more details, see [`GenericDatabase`]
245#[cfg(feature = "alloc")]
246pub type SharedDatabase = GenericDatabase<crate::container::ArcContainer<SandiaDecayDataBase>>;
247/// Initialized database stored in wherever the `&`[`core::mem::MaybeUninit`] pointed to
248///
249/// For more details, see [`GenericDatabase`]
250pub type LocalDatabase<'l> = GenericDatabase<RefContainer<'l, SandiaDecayDataBase>>;
251
252impl<C: Container<Inner = SandiaDecayDataBase>> GenericDatabase<C> {
253 /// Attempts to create initialized database via path to the database `.xml` file
254 ///
255 /// This is the same as consequent [`UninitDatabase::new`] and [`UninitDatabase::init`] calls
256 ///
257 /// ### Returns
258 /// - [`Result::Ok`] successfully initialized database
259 /// - [`Result::Err`] contains a description of panic from C++ side
260 ///
261 /// ### Example
262 /// An example using [`crate::container::BoxContainer`] for storage:
263 /// ```rust,no_run
264 /// # #[cfg(feature = "alloc")] {
265 /// # use sdecay::database::Database;
266 /// // assuming `database.xml` contains database data
267 /// let database = Database::from_path("database.xml")
268 /// .expect("`database.xml` should exist and contain a valid database data");
269 /// # }
270 /// ```
271 ///
272 /// Note, that path can be any [`AsCppString`] implementor - see it's doc to find the most convenient for you
273 #[inline]
274 pub fn from_path_in(
275 allocator: C::Allocator,
276 path: impl AsCppString,
277 ) -> Result<Self, CppException> {
278 match GenericUninitDatabase::new_in(allocator).init(path) {
279 Ok(init) => Ok(init),
280 Err((_, error)) => Err(error),
281 }
282 }
283
284 /// Same as [`Self::from_path_in`], but uses `C::Allocator`'s [`Default`] implementation to obtain the allocator
285 #[inline]
286 pub fn from_path(path: impl AsCppString) -> Result<Self, CppException>
287 where
288 C::Allocator: Default,
289 {
290 Self::from_path_in(C::Allocator::default(), path)
291 }
292
293 /// Attempts to create initialized database via `xml` data
294 ///
295 /// This is the same as consequent [`UninitDatabase::new`] and [`UninitDatabase::init_bytes`] calls
296 ///
297 /// ### Returns
298 /// - [`Result::Ok`] successfully initialized database
299 /// - [`Result::Err`] contains a description of panic from C++ side
300 ///
301 /// ### Example
302 /// An example using [`crate::container::BoxContainer`] for storage:
303 /// ```rust,no_run
304 /// # #[cfg(feature = "alloc")] {
305 /// # use sdecay::database::Database;
306 /// let data: &[u8] = br#"<?xml version="1.0"?><document>...</document>"#; // assuming `data contains valid database data`
307 /// let database = Database::from_bytes(data)
308 /// .expect("Should provide valid database data");
309 /// # }
310 /// ```
311 #[inline]
312 pub fn from_bytes_in(
313 allocator: C::Allocator,
314 bytes: impl AsRef<[u8]>,
315 ) -> Result<Self, CppException> {
316 match GenericUninitDatabase::new_in(allocator).init_bytes(bytes) {
317 Ok(init) => Ok(init),
318 Err((_, error)) => Err(error),
319 }
320 }
321
322 /// Same as [`Self::from_bytes_in`], but uses `C::Allocator`'s [`Default`] implementation to obtain the allocator
323 #[inline]
324 pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self, CppException>
325 where
326 C::Allocator: Default,
327 {
328 Self::from_bytes_in(C::Allocator::default(), bytes)
329 }
330
331 /// Attempts to create initialized database by a path from `SANDIA_DATABASE_PATH` environment variable
332 ///
333 /// This is the same as consequent [`UninitDatabase::new`] and [`UninitDatabase::init_bytes`] calls
334 ///
335 /// ### Returns
336 /// - [`Result::Ok`] successfully initialized database
337 /// - [`Result::Err`] contains a description of panic from C++ side
338 ///
339 /// ### Example
340 /// An example using [`crate::container::BoxContainer`] for storage:
341 /// ```rust,no_run
342 /// # use sdecay::database::Database;
343 /// // assuming `SANDIA_DATABASE_PATH` envvar contains path to database file
344 /// let database = Database::from_env()
345 /// .expect("`SANDIA_DATABASE_PATH` should contain path to data, and data should be valid");
346 /// ```
347 #[cfg(feature = "std")]
348 #[inline]
349 pub fn from_env_in(allocator: C::Allocator) -> Result<Self, EnvInitError> {
350 match GenericUninitDatabase::new_in(allocator).init_env() {
351 Ok(init) => Ok(init),
352 Err((_, error)) => Err(error),
353 }
354 }
355
356 /// Same as [`Self::from_env_in`], but uses `C::Allocator`'s [`Default`] implementation to obtain the allocator
357 #[cfg(feature = "std")]
358 #[inline]
359 pub fn from_env() -> Result<Self, EnvInitError>
360 where
361 C::Allocator: Default,
362 {
363 Self::from_env_in(C::Allocator::default())
364 }
365
366 /// Creates initialized database from embedded "default" database
367 ///
368 /// This is the same as consequent [`UninitDatabase::new`] and [`UninitDatabase::init_vendor`] calls
369 ///
370 /// ### Example
371 /// Using [`crate::container::BoxContainer`] as storage:
372 /// ```rust
373 /// # #[cfg(feature = "alloc")] {
374 /// # use sdecay::database::Database;
375 /// // assuming `database` feature is enabled
376 /// let database = Database::vendor();
377 /// # }
378 /// ```
379 #[cfg(feature = "database")]
380 #[inline]
381 pub fn vendor_in(allocator: C::Allocator) -> Self {
382 GenericUninitDatabase::new_in(allocator).init_vendor()
383 }
384
385 /// Same as [`Self::vendor_in`], but uses `C::Allocator`'s [`Default`] implementation to obtain the allocator
386 #[cfg(feature = "database")]
387 #[inline]
388 pub fn vendor() -> Self
389 where
390 C::Allocator: Default,
391 {
392 Self::vendor_in(C::Allocator::default())
393 }
394
395 /// Creates initialized database from embedded "min" database
396 ///
397 /// This is the same as consequent [`UninitDatabase::new`] and [`UninitDatabase::init_vendor_min`] calls
398 ///
399 /// ### Example
400 /// Using [`crate::container::BoxContainer`] as storage:
401 /// ```rust
402 /// # #[cfg(feature = "alloc")] {
403 /// # use sdecay::database::Database;
404 /// // assuming `database-min` feature is enabled
405 /// let database = Database::vendor_min();
406 /// # }
407 /// ```
408 #[cfg(feature = "database-min")]
409 #[inline]
410 pub fn vendor_min_in(allocator: C::Allocator) -> Self {
411 GenericUninitDatabase::new_in(allocator).init_vendor_min()
412 }
413
414 /// Same as [`Self::vendor_min_in`], but uses `C::Allocator`'s [`Default`] implementation to obtain the allocator
415 #[cfg(feature = "database-min")]
416 #[inline]
417 pub fn vendor_min() -> Self
418 where
419 C::Allocator: Default,
420 {
421 Self::vendor_min_in(C::Allocator::default())
422 }
423
424 /// Creates initialized database from embedded "nocoinc-min" database
425 ///
426 /// This is the same as consequent [`UninitDatabase::new`] and [`UninitDatabase::init_vendor_nocoinc_min`] calls
427 ///
428 /// ### Example
429 /// Using [`crate::container::BoxContainer`] as storage:
430 /// ```rust
431 /// # #[cfg(feature = "alloc")] {
432 /// # use sdecay::database::Database;
433 /// let database = Database::vendor_nocoinc_min();
434 /// # }
435 /// ```
436 #[cfg(feature = "database-nocoinc-min")]
437 #[inline]
438 pub fn vendor_nocoinc_min_in(allocator: C::Allocator) -> Self {
439 GenericUninitDatabase::new_in(allocator).init_vendor_nocoinc_min()
440 }
441
442 /// Same as [`Self::vendor_nocoinc_min_in`], but uses `C::Allocator`'s [`Default`] implementation to obtain the allocator
443 #[cfg(feature = "database-nocoinc-min")]
444 #[inline]
445 pub fn vendor_nocoinc_min() -> Self
446 where
447 C::Allocator: Default,
448 {
449 Self::vendor_nocoinc_min_in(C::Allocator::default())
450 }
451
452 /// Resets the database, returning it into uninitialized (empty) state
453 ///
454 /// Note, that this call **is not required** to properly drop the database - all of the resources are freed upon drop
455 ///
456 /// ### Returns
457 /// [`Option::None`] represents failure to reset the database, due to it being shared by multiple containers
458 ///
459 /// ### Example
460 /// ```rust
461 /// # #[cfg(feature = "std")] {
462 /// // database stored in the `Box` can always be reset:
463 /// # use sdecay::database::Database;
464 /// let database = Database::from_env().unwrap();
465 /// let _uninit = database.reset().expect("Should be able to reset a database behind exclusive pointer");
466 ///
467 /// // database stored in the `Arc` can be reset while not shared:
468 /// # use sdecay::database::SharedDatabase;
469 /// let shared_database = SharedDatabase::from_env().unwrap();
470 /// let _uninit = shared_database.reset().expect("Should be able to reset the database, while it is not shared");
471 ///
472 /// // once shared, databased can no longer be dropped:
473 /// let shared_database = SharedDatabase::from_env().unwrap();
474 /// let shared_database2 = shared_database.clone();
475 /// let _ = shared_database.reset().expect_err("Should not reset database in a shared state");
476 /// // once not shared, reset is possible once again:
477 /// let _uninit = shared_database2.reset().expect("Database is not longer shared, should be able to reset");
478 /// # }
479 /// ```
480 #[inline]
481 pub fn reset(mut self) -> Result<GenericUninitDatabase<C>, Self> {
482 let Some(pin) = self.0.try_inner() else {
483 return Err(self);
484 };
485 pin.reset();
486 Ok(GenericUninitDatabase(self.0))
487 }
488}
489
490impl<C: Container<Inner = SandiaDecayDataBase>> Deref for GenericDatabase<C> {
491 type Target = SandiaDecayDataBase;
492
493 #[inline]
494 fn deref(&self) -> &Self::Target {
495 &self.0
496 }
497}