geekorm_core/backends/connect/
mod.rs

1//! # Connection
2//!
3//! ## Example
4//!
5//! ```rust
6//! # #[cfg(feature = "libsql")] {
7//!
8//! use geekorm_core::backends::connect::{ConnectionManager, Connection};
9//!
10//! #[tokio::main]
11//! async fn main() {
12//!     let manager = ConnectionManager::connect(":memory:").await.unwrap();
13//!
14//!     let connection = manager.acquire().await;
15//!
16//!     // ... do stuff with the connection
17//! }
18//!
19//! # }
20//! ```
21//!
22
23use std::fmt::{Debug, Display};
24use std::path::PathBuf;
25use std::sync::atomic::AtomicUsize;
26use url::Url;
27
28pub mod backend;
29pub mod manager;
30
31pub use manager::ConnectionManager;
32
33/// A connection to a database backend.
34///
35/// This is a wrapper around the actual connection to the database.
36pub struct Connection<'a> {
37    pool: &'a ConnectionManager,
38    query_count: AtomicUsize,
39    backend: Backend,
40}
41
42/// Backend is an enum that represents the different types of backends that
43/// can be used to connect to a database.
44#[derive(Default, Clone)]
45pub enum Backend {
46    /// A libsql connection
47    #[cfg(feature = "libsql")]
48    Libsql {
49        /// The inner connection
50        conn: ::libsql::Connection,
51    },
52    /// A rusqlite connection
53    #[cfg(feature = "rusqlite")]
54    Rusqlite {
55        /// The inner connection
56        conn: std::sync::Arc<::rusqlite::Connection>,
57    },
58    /// Unknown backend
59    #[default]
60    Unknown,
61}
62
63/// Connection Type
64#[derive(Debug, Default, Clone, PartialEq, Eq)]
65pub enum ConnectionType {
66    /// In-memory database
67    #[default]
68    InMemory,
69    /// File-based database
70    Path {
71        /// Database file path
72        file: PathBuf,
73    },
74    /// Remote database
75    Remote {
76        /// Database URL
77        url: Url,
78    },
79}
80
81impl Connection<'_> {
82    /// Count the number of queries that have been executed since the
83    /// connection was created.
84    ///
85    /// This is useful for testing to ensure that the expected number
86    /// of queries have been executed against the database.
87    pub fn count(&self) -> usize {
88        self.query_count.load(std::sync::atomic::Ordering::Relaxed)
89    }
90}
91
92impl Display for Connection<'_> {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        match &self.backend {
95            #[cfg(feature = "libsql")]
96            Backend::Libsql { .. } => {
97                write!(f, "Backend::Libsql({})", self.pool.get_database_type())
98            }
99            #[cfg(feature = "rusqlite")]
100            Backend::Rusqlite { .. } => {
101                write!(f, "Backend::Rusqlite({})", self.pool.get_database_type())
102            }
103            Backend::Unknown => write!(f, "Backend::Unknown"),
104        }
105    }
106}
107
108impl Drop for Connection<'_> {
109    fn drop(&mut self) {
110        // On drop, we put the connection back into the pool
111        // TODO: Can we remove this clone?
112        self.pool.insert_backend(self.backend.clone());
113    }
114}
115
116impl Debug for Connection<'_> {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        match self.backend {
119            #[cfg(feature = "libsql")]
120            Backend::Libsql { .. } => write!(f, "Backend::Libsql"),
121            #[cfg(feature = "rusqlite")]
122            Backend::Rusqlite { .. } => write!(f, "Backend::Rusqlite"),
123            Backend::Unknown => write!(f, "Backend::Unknown"),
124        }
125    }
126}
127
128impl Display for ConnectionType {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        match self {
131            ConnectionType::InMemory => write!(f, "InMemory"),
132            ConnectionType::Path { .. } => write!(f, "Path"),
133            ConnectionType::Remote { .. } => write!(f, "Remote"),
134        }
135    }
136}