parsql_sqlite/lib.rs
1//! # parsql-sqlite
2//!
3//! SQLite integration for parsql.
4//! This crate provides synchronous APIs for working with SQLite databases.
5//!
6//! ## Features
7//!
8//! - Synchronous SQLite operations
9//! - Automatic SQL query generation
10//! - Secure parameter management
11//! - Generic CRUD operations
12//! - Transaction support
13//! - Extension methods for the Connection object
14//!
15//! ## Usage
16//!
17//! ```rust,no_run
18//! use rusqlite::{Connection, Result};
19//! use parsql::sqlite::{get, insert};
20//!
21//! #[derive(Insertable, SqlParams)]
22//! #[table("users")]
23//! pub struct InsertUser {
24//! pub name: String,
25//! pub email: String,
26//! }
27//!
28//! #[derive(Queryable, SqlParams, FromRow)]
29//! #[table("users")]
30//! #[where_clause("id = ?")]
31//! pub struct GetUser {
32//! pub id: i32,
33//! pub name: String,
34//! pub email: String,
35//! }
36//!
37//! fn main() -> Result<()> {
38//! let conn = Connection::open("test.db")?;
39//!
40//! // Insert a new user
41//! let insert_user = InsertUser {
42//! name: "John".to_string(),
43//! email: "john@example.com".to_string(),
44//! };
45//!
46//! let id = insert(&conn, insert_user)?;
47//!
48//! // Get the user back
49//! let get_user = GetUser::new(id as i32);
50//! let user = get(&conn, &get_user)?;
51//!
52//! println!("User: {:?}", user);
53//! Ok(())
54//! }
55//! ```
56//!
57//! ## Using Extension Methods
58//!
59//! You can also use the extension methods directly on the Connection object:
60//!
61//! ```rust,no_run
62//! use rusqlite::{Connection, Result};
63//! use parsql::sqlite::CrudOps; // Import the trait
64//! use parsql::sqlite::macros::{Insertable, SqlParams, Queryable, FromRow};
65//!
66//! #[derive(Insertable, SqlParams)]
67//! #[table("users")]
68//! pub struct InsertUser {
69//! pub name: String,
70//! pub email: String,
71//! }
72//!
73//! #[derive(Queryable, FromRow, SqlParams)]
74//! #[table("users")]
75//! #[where_clause("id = ?")]
76//! pub struct GetUser {
77//! pub id: i32,
78//! pub name: String,
79//! pub email: String,
80//! }
81//!
82//! fn main() -> Result<()> {
83//! let conn = Connection::open("test.db")?;
84//!
85//! // Insert a new user using extension method
86//! let insert_user = InsertUser {
87//! name: "John".to_string(),
88//! email: "john@example.com".to_string(),
89//! };
90//!
91//! let rows_affected = conn.insert(insert_user)?;
92//!
93//! // Get the user back using extension method
94//! let get_user = GetUser {
95//! id: 1,
96//! name: String::new(),
97//! email: String::new(),
98//! };
99//! let user = conn.get(&get_user)?;
100//!
101//! println!("User: {:?}", user);
102//! Ok(())
103//! }
104//! ```
105//!
106//! ## Using Transactions
107//!
108//! You can perform database operations within a transaction to ensure atomicity:
109//!
110//! ```rust,no_run
111//! use rusqlite::{Connection, Result};
112//! use parsql::sqlite::transactional;
113//! use parsql::macros::{Insertable, SqlParams, Updateable, UpdateParams};
114//!
115//! #[derive(Insertable, SqlParams)]
116//! #[table("users")]
117//! struct InsertUser {
118//! name: String,
119//! email: String,
120//! }
121//!
122//! #[derive(Updateable, UpdateParams)]
123//! #[table("users")]
124//! #[update("email")]
125//! #[where_clause("id = ?")]
126//! struct UpdateUser {
127//! id: i64,
128//! email: String,
129//! }
130//!
131//! fn main() -> Result<()> {
132//! let conn = Connection::open("test.db")?;
133//!
134//! // Begin a transaction
135//! let tx = transactional::begin(&conn)?;
136//!
137//! // Insert a user within the transaction
138//! let insert_user = InsertUser {
139//! name: "John".to_string(),
140//! email: "john@example.com".to_string(),
141//! };
142//! let (tx, _) = transactional::tx_insert(tx, insert_user)?;
143//!
144//! // Update the user within the same transaction
145//! let update_user = UpdateUser {
146//! id: 1,
147//! email: "john.updated@example.com".to_string(),
148//! };
149//! let (tx, _) = transactional::tx_update(tx, update_user)?;
150//!
151//! // Commit the transaction - both operations succeed or fail together
152//! tx.commit()?;
153//!
154//! Ok(())
155//! }
156//! ```
157
158pub mod crud_ops;
159pub mod transactional_ops;
160
161// Re-export sqlite types that might be needed
162pub use rusqlite::{Connection, Error, Row};
163pub use rusqlite::types::ToSql;
164
165// Re-export crud operations
166pub use crud_ops::{
167 insert,
168 select,
169 select_all,
170 update,
171 delete,
172 get,
173 get_all,
174 CrudOps,
175};
176
177// Re-export transaction operations
178pub use transactional_ops as transactional;
179
180pub use parsql_macros as macros;
181
182/// Trait for generating SQL queries.
183/// This trait is implemented by the derive macro `Queryable`, `Insertable`, `Updateable`, and `Deletable`.
184pub trait SqlQuery {
185 /// Returns the SQL query string.
186 fn query() -> String;
187}
188
189/// Trait for providing SQL parameters.
190/// This trait is implemented by the derive macro `SqlParams`.
191pub trait SqlParams {
192 /// Returns a vector of references to SQL parameters.
193 fn params(&self) -> Vec<&(dyn ToSql + Sync)>;
194}
195
196/// Trait for providing UPDATE parameters.
197/// This trait is implemented by the derive macro `UpdateParams`.
198pub trait UpdateParams {
199 /// Returns a vector of references to SQL parameters for UPDATE operations.
200 fn params(&self) -> Vec<&(dyn ToSql + Sync)>;
201}
202
203/// Trait for converting database rows to Rust structs.
204/// This trait is implemented by the derive macro `FromRow`.
205pub trait FromRow {
206 /// Converts a database row to a Rust struct.
207 ///
208 /// # Arguments
209 /// * `row` - A reference to a database row
210 ///
211 /// # Returns
212 /// * `Result<Self, Error>` - The converted struct or an error
213 fn from_row(row: &Row) -> Result<Self, Error>
214 where
215 Self: Sized;
216}