geekorm_core/backends/
mod.rs

1//! # Backend Module for GeekORM
2//!
3//! **Example:**
4//!
5//! Here is an example of how to use GeekORM with a mock connection.
6//!
7//! ```no_run
8//! # #[cfg(feature = "backends")] {
9//! # use anyhow::Result;
10//! use geekorm::prelude::*;
11//!
12//! # #[derive(Debug, Clone)]
13//! # struct Connection;
14//! # impl GeekConnection for Connection {
15//! #     type Connection = Self;
16//! # }
17//!
18//! #[derive(Table, Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
19//! pub struct Users {
20//!     #[geekorm(primary_key, auto_increment)]
21//!     pub id: PrimaryKey<i32>,
22//!     #[geekorm(unique)]
23//!     pub username: String,
24//! }
25//!
26//! #[tokio::main]
27//! async fn main() -> Result<()> {
28//!     // Create a new connection (this is a mock connection)
29//!     let connection = Connection {};
30//!
31//!     Users::create_table(&connection).await?;
32//!
33//!     let users = vec!["geekmasher", "bob", "alice", "eve", "mallory", "trent"];
34//!     for user in users {
35//!         let mut new_user = Users::new(user);
36//!         new_user.save(&connection).await?;
37//!     }
38//!
39//!     // Fetch or create a user
40//!     let mut geek = Users::new("geekmasher");
41//!     geek.fetch_or_create(&connection).await?;
42//!     
43//!     // Fetch a user by their username (exact match)
44//!     let geekmasher = Users::fetch_by_username(&connection, "geekmasher").await?;
45//!
46//!     // Search for a user (partial match)
47//!     let search = Users::search(&connection, "geek").await?;
48//!     # assert_eq!(search.len(), 1);
49//!
50//!     // Fetch first and last user
51//!     let first_user = Users::first(&connection).await?;
52//!     # assert_eq!(first_user.username, "geekmasher");
53//!     let last_user = Users::last(&connection).await?;
54//!     # assert_eq!(last_user.username, "trent");
55//!
56//!     // Delete the user
57//!     geek.delete(&connection).await?;
58//!
59//!     Ok(())
60//! }
61//! # }
62//! ```
63
64use std::collections::HashMap;
65
66use crate::{Query, QueryBuilder, QueryBuilderTrait, TableBuilder, TablePrimaryKey, Value};
67
68#[cfg(feature = "libsql")]
69pub mod libsql;
70#[cfg(feature = "rusqlite")]
71pub mod rusqlite;
72
73/// GeekConnection is the trait used for models to interact with the database.
74///
75/// This trait is used to define the methods that are used to interact with the database.
76pub trait GeekConnector<'a, C>
77where
78    C: GeekConnection<Connection = C> + 'a,
79    Self: Sized + TableBuilder + QueryBuilderTrait + serde::Serialize + serde::de::DeserializeOwned,
80{
81    /// Query the database with an active Connection and Query
82    #[allow(async_fn_in_trait, unused_variables)]
83    async fn query(connection: &'a C, query: Query) -> Result<Vec<Self>, crate::Error> {
84        C::query::<Self>(connection, query).await
85    }
86
87    /// Query the first row from the database with an active Connection and Query
88    #[allow(async_fn_in_trait, unused_variables)]
89    async fn query_first(connection: &'a C, query: Query) -> Result<Self, crate::Error> {
90        C::query_first::<Self>(connection, query).await
91    }
92
93    /// Execute a query on the database and do not return any rows
94    #[allow(async_fn_in_trait, unused_variables)]
95    async fn execute(connection: &'a C, query: Query) -> Result<(), crate::Error> {
96        C::execute(connection, query).await
97    }
98
99    /// Create a table in the database
100    #[allow(async_fn_in_trait, unused_variables)]
101    async fn create_table(connection: &'a C) -> Result<(), crate::Error> {
102        C::create_table::<Self>(connection).await
103    }
104
105    /// Count the number of rows based on a Query
106    #[allow(async_fn_in_trait, unused_variables)]
107    async fn row_count(connection: &'a C, query: Query) -> Result<i64, crate::Error> {
108        C::row_count(connection, query).await
109    }
110
111    /// Count the total number of rows in the table
112    #[allow(async_fn_in_trait, unused_variables)]
113    async fn total(connection: &'a C) -> Result<i64, crate::Error> {
114        C::row_count(
115            connection,
116            Self::query_count().table(Self::table()).build()?,
117        )
118        .await
119    }
120
121    /// Fetch all rows from the table
122    #[allow(async_fn_in_trait, unused_variables)]
123    async fn all(connection: &'a C) -> Result<Vec<Self>, crate::Error> {
124        C::query::<Self>(
125            connection,
126            Self::query_select().table(Self::table()).build()?,
127        )
128        .await
129    }
130
131    /// Fetch by Page
132    #[cfg(feature = "pagination")]
133    #[allow(async_fn_in_trait, unused_variables)]
134    async fn page(connection: &'a C, page: &crate::Page) -> Result<Vec<Self>, crate::Error> {
135        C::query::<Self>(
136            connection,
137            QueryBuilder::select()
138                .table(Self::table())
139                .page(page)
140                .build()?,
141        )
142        .await
143    }
144
145    /// Create a new Pagination instance with the current table and fetch
146    /// total number of rows
147    #[cfg(feature = "pagination")]
148    #[allow(async_fn_in_trait, unused_variables)]
149    async fn paginate(connection: &'a C) -> Result<crate::Pagination<Self>, crate::Error> {
150        let mut page = crate::Pagination::new();
151        page.set_total(Self::total(connection).await? as u32);
152        Ok(page)
153    }
154
155    /// Update the current object in the database
156    #[allow(async_fn_in_trait, unused_variables)]
157    async fn update(&mut self, connection: &'a C) -> Result<(), crate::Error> {
158        C::execute(connection, Self::query_update(self)).await
159    }
160
161    /// Save the current object to the database
162    #[allow(async_fn_in_trait, unused_variables)]
163    async fn save(&mut self, connection: &'a C) -> Result<(), crate::Error>;
164
165    /// Delete the current object from the database
166    #[allow(async_fn_in_trait, unused_variables)]
167    async fn delete(&self, connection: &'a C) -> Result<(), crate::Error> {
168        C::execute(connection, Self::query_delete(self)).await
169    }
170
171    /// Fetches all of the foreign key values for the current object
172    #[allow(async_fn_in_trait, unused_variables)]
173    async fn fetch(&mut self, connection: &'a C) -> Result<(), crate::Error>;
174
175    /// Filter the rows in the table based on specific criteria passed as a tuple of (&str, Value).
176    ///
177    /// You can use prefix operators to define the type of comparison to use:
178    ///
179    /// - `=`: Equal
180    /// - `~`: Like
181    /// - `!`: Not equal
182    ///
183    /// If no prefix is used, the default comparison is equal.
184    #[allow(async_fn_in_trait, unused_variables)]
185    async fn filter(
186        connection: &'a C,
187        fields: Vec<(&str, impl Into<Value>)>,
188    ) -> Result<Vec<Self>, crate::Error> {
189        Self::query(
190            connection,
191            Self::query_select()
192                .table(Self::table())
193                .filter(fields)
194                .build()?,
195        )
196        .await
197    }
198
199    /// Filter with Pagination
200    #[cfg(feature = "pagination")]
201    #[allow(async_fn_in_trait, unused_variables)]
202    async fn filter_page(
203        connection: &'a C,
204        fields: Vec<(&str, impl Into<Value>)>,
205        page: &crate::Page,
206    ) -> Result<Vec<Self>, crate::Error> {
207        Self::query(
208            connection,
209            Self::query_select()
210                .table(Self::table())
211                .filter(fields)
212                .page(page)
213                .build()?,
214        )
215        .await
216    }
217
218    /// Fetch all rows from the database
219    #[deprecated(
220        since = "0.8.4",
221        note = "Please use the `all` method instead of `fetch_all`"
222    )]
223    #[allow(async_fn_in_trait, unused_variables)]
224    async fn fetch_all(connection: &'a C) -> Result<Vec<Self>, crate::Error> {
225        C::query::<Self>(
226            connection,
227            QueryBuilder::select().table(Self::table()).build()?,
228        )
229        .await
230    }
231
232    /// Fetch or create a row in the database
233    #[allow(async_fn_in_trait, unused_variables)]
234    async fn fetch_or_create(&mut self, connection: &'a C) -> Result<(), crate::Error>;
235
236    /// Search for a row in the database based on specific criteria
237    #[cfg(feature = "search")]
238    #[allow(async_fn_in_trait, unused_variables)]
239    async fn search(
240        connection: &'a C,
241        search: impl Into<String>,
242    ) -> Result<Vec<Self>, crate::Error>;
243
244    /// Fetch the first row from the database (based on the primary key)
245    #[allow(async_fn_in_trait, unused_variables)]
246    async fn first(connection: &'a C) -> Result<Self, crate::Error>
247    where
248        Self: TablePrimaryKey,
249    {
250        C::query_first::<Self>(
251            connection,
252            Self::query_select()
253                .table(Self::table())
254                .order_by(
255                    &Self::primary_key(),
256                    crate::builder::models::QueryOrder::Asc,
257                )
258                .limit(1)
259                .build()?,
260        )
261        .await
262    }
263
264    /// Fetch last row from the database (based on the primary key)
265    #[allow(async_fn_in_trait, unused_variables)]
266    async fn last(connection: &'a C) -> Result<Self, crate::Error>
267    where
268        Self: TablePrimaryKey,
269    {
270        C::query_first::<Self>(
271            connection,
272            Self::query_select()
273                .table(Self::table())
274                .order_by(
275                    &Self::primary_key(),
276                    crate::builder::models::QueryOrder::Desc,
277                )
278                .limit(1)
279                .build()?,
280        )
281        .await
282    }
283}
284
285/// GeekConnection is the trait that all backends must implement to be able
286/// to interact with the database.
287pub trait GeekConnection {
288    /// Native Connection
289    type Connection;
290
291    /// Create a table in the database
292    #[allow(async_fn_in_trait, unused_variables)]
293    async fn create_table<T>(connection: &Self::Connection) -> Result<(), crate::Error>
294    where
295        T: TableBuilder
296            + QueryBuilderTrait
297            + Sized
298            + serde::Serialize
299            + serde::de::DeserializeOwned,
300    {
301        Err(crate::Error::NotImplemented)
302    }
303
304    /// Run a SELECT Count query on the database and return the number of rows
305    #[allow(async_fn_in_trait, unused_variables)]
306    async fn row_count(connection: &Self::Connection, query: Query) -> Result<i64, crate::Error> {
307        Err(crate::Error::NotImplemented)
308    }
309
310    /// Execute a query on the database and do not return any rows
311    #[allow(async_fn_in_trait, unused_variables)]
312    async fn execute(connection: &Self::Connection, query: Query) -> Result<(), crate::Error> {
313        Err(crate::Error::NotImplemented)
314    }
315
316    /// Execute a batch query on the database and do not return any rows
317    #[allow(async_fn_in_trait, unused_variables)]
318    async fn batch(connection: &Self::Connection, query: Query) -> Result<(), crate::Error> {
319        Err(crate::Error::NotImplemented)
320    }
321
322    /// Query the database with an active Connection and Query
323    #[allow(async_fn_in_trait, unused_variables)]
324    async fn query<T>(connection: &Self::Connection, query: Query) -> Result<Vec<T>, crate::Error>
325    where
326        T: serde::de::DeserializeOwned,
327    {
328        Err(crate::Error::NotImplemented)
329    }
330
331    /// Query the database with an active Connection and Query and return the first row.
332    ///
333    /// Note: Make sure the query is limited to 1 row to avoid retrieving multiple rows
334    /// and only using the first one.
335    #[allow(async_fn_in_trait, unused_variables)]
336    async fn query_first<T>(connection: &Self::Connection, query: Query) -> Result<T, crate::Error>
337    where
338        T: serde::de::DeserializeOwned,
339    {
340        Err(crate::Error::NotImplemented)
341    }
342
343    /// Query the database with an active Connection and Query and return a list of GeekORM Values.
344    #[allow(async_fn_in_trait, unused_variables)]
345    async fn query_raw(
346        connection: &Self::Connection,
347        query: Query,
348    ) -> Result<Vec<HashMap<String, Value>>, crate::Error> {
349        Err(crate::Error::NotImplemented)
350    }
351
352    /// Get Table Names
353    #[cfg(feature = "migrations")]
354    #[allow(async_fn_in_trait, unused_variables)]
355    async fn table_names(connection: &Self::Connection) -> Result<Vec<String>, crate::Error> {
356        // TODO: This only works for SQLite
357        let results: Vec<TableNames> = Self::query(
358            connection,
359            Query {
360                query: "SELECT name FROM sqlite_master WHERE type='table'".to_string(),
361                query_type: crate::builder::models::QueryType::Select,
362                ..Default::default()
363            },
364        )
365        .await?;
366
367        // Make sure to not include `sqlite_sequence` table
368        Ok(results
369            .iter()
370            .filter_map(|table| {
371                if table.name != "sqlite_sequence" {
372                    Some(table.name.clone())
373                } else {
374                    None
375                }
376            })
377            .collect())
378    }
379
380    /// Pragma table info
381    #[cfg(feature = "migrations")]
382    #[allow(async_fn_in_trait, unused_variables)]
383    async fn pragma_info(
384        connection: &Self::Connection,
385        table: &str,
386    ) -> Result<Vec<TableInfo>, crate::Error> {
387        Self::query(
388            connection,
389            Query {
390                query: format!("PRAGMA table_info({})", table),
391                query_type: crate::builder::models::QueryType::Select,
392                ..Default::default()
393            },
394        )
395        .await
396    }
397}
398
399/// Table Info
400#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
401pub struct TableInfo {
402    /// The column ID
403    pub cid: i32,
404    /// The column name
405    pub name: String,
406    /// The column type
407    #[serde(rename = "type")]
408    pub coltype: String,
409    /// The column notnull value
410    pub notnull: i32,
411    /// The column default value
412    pub dflt_value: Option<String>,
413    /// The column primary key value
414    pub pk: i32,
415}
416
417/// Table Names
418#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
419struct TableNames {
420    /// The table name
421    pub name: String,
422}