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