derive_sql/
lib.rs

1//! The crate `derive_sql` is articulated around:
2//! - traits that allow SQL database drivers (`rusqlite` and `mysql`) to provide
3//!   defined set of functionalities;
4//! - traits that scaffold on these common functionalities to wrap common
5//!   SQL statement functions into typed interfaces.
6//!
7//! The trait `Connection` provides a uniform interface for interaction with SQL
8//! database drivers via `query` calls where all elements of the query are encompassed
9//! in the statement; and `execute` calls where the statement refers to parameters
10//! to be provided.
11//!
12//! ```rust
13//! use derive_sql::traits;
14//!
15//! // Function to create table, populate table with an entry, and retrieve the first entry field name
16//! fn run<S, R>(s: &mut S) -> String 
17//! where S: traits::Connection<R>,
18//!       R: traits::Row,
19//! {
20//!   s.query_drop("DROP TABLE IF EXISTS example_table").unwrap();
21//!
22//!   s.query_drop("CREATE TABLE example_table (
23//!     id INTEGER,
24//!     name TEXT
25//!   )").unwrap(); 
26//!   
27//!   s.execute_with_params("INSERT INTO example_table (id, name) VALUES (?, ?)",
28//!     &(1i64, "Jane Doe".to_string())
29//!   ).unwrap();
30//!   
31//!   let r: String = s.query_first("SELECT name FROM example_table").unwrap()
32//!   .map(|r| r.get(0).unwrap().unwrap())
33//!   .unwrap();
34//!
35//!   r
36//! }
37//!   
38//! // Called using a SQLite connection via rusqlite
39#![cfg_attr(not(feature="sqlite"), doc = "/*")]
40//! let mut conn = rusqlite::Connection::open_in_memory().unwrap();
41//! assert!(run(&mut conn).eq("Jane Doe"));
42#![cfg_attr(not(feature="sqlite"), doc = "*/")]
43//!
44//! // Call using a MySQL connection
45#![cfg_attr(not(feature="mysql"), doc = "/*")]
46//! let mut mysql_conn = mysql::Conn::new(
47//!   mysql::Opts::from_url("mysql://test@localhost/simpledb").unwrap()
48//! ).unwrap();
49//! assert!(run(&mut mysql_conn).eq("Jane Doe"));
50#![cfg_attr(not(feature="mysql"), doc = "*/")]
51//! ```
52//!
53//! The following is showing the same test using a set of traits provided to standardize the interaction with SQL
54//! databases:
55//!
56//! ```rust
57//! use derive_sql::traits::{self, Table, TableStatement, SelectV2, SelectStatement, Insert, InsertStatement,
58//!   Delete, DeleteStatement, 
59//!   Update, UpdateStatement, 
60//!   Param, Params, Row, TryFromRefRow,
61//!   ToParam,
62//! };
63//! use derive_sql::structs::{filter, Field};
64//! use derive_sql::Result;
65//!
66//! // Define a `struct` representing the data to be stored
67//! /* #[derive(Clone)] */
68//! struct People {
69//!   id: i64,
70//!   name: String,
71//! }
72//!
73//! // Implement traits to convert `People` to and from
74//! impl Params for People {
75//!   fn as_vec_params(&self) -> Result<Vec<Param>> {
76//!     Ok(vec![self.id.to_param()?, self.name.to_param()?])
77//!   }
78//! }
79//! impl<R> TryFromRefRow<R> for People 
80//! where R: Row,
81//! {
82//!   fn try_from(r: &R) -> Result<Self> {
83//!     Ok(People {
84//!       id: r.get(0).ok_or(derive_sql::Error::RowItemNotFound(0))??,
85//!       name: r.get(1).ok_or(derive_sql::Error::RowItemNotFound(1))??,
86//!     })
87//!   }
88//! }
89//!
90//! #[derive(Default)]
91//! struct SqlPeople {}
92//!
93//! // Implement traits to manipulate the data using struct `SqlPeople`
94//! impl TableStatement for SqlPeople {
95//!   fn create_stmt(&self)              -> Result<String> { Ok(format!("CREATE TABLE example2_table ( id INTEGER, name TEXT )")) }
96//!   fn create_if_not_exist_stmt(&self) -> Result<String> { Ok(format!("CREATE TABLE IF NOT EXISTS example2_table ( id INTEGER, name TEXT )")) }
97//!   fn drop_stmt(&self)                -> Result<String> { Ok(format!("DROP TABLE IF EXISTS example2_table")) }
98//! }
99//! impl SelectStatement for SqlPeople {
100//!   fn select_stmt(&self) -> Result<String> { Ok(format!("SELECT id,name FROM example2_table")) }
101//! }
102//! impl InsertStatement for SqlPeople {
103//!   fn insert_stmt(&self) -> Result<String> { Ok(format!("INSERT INTO example2_table (id, name) VALUES (?, ?)")) }
104//! }
105//! impl UpdateStatement for SqlPeople {
106//!   fn update_stmt(&self) -> Result<String> { Ok(format!("UPDATE example2_table SET `id` = ?, `name` = ?")) }
107//! }
108//! impl DeleteStatement for SqlPeople {
109//!   fn delete_stmt(&self) -> Result<String> { Ok(format!("DELETE FROM example2_table")) }
110//! }
111//!
112//! fn run<S, R>(s: &mut S) -> String
113//! where S: traits::Connection<R>,
114//!       R: traits::Row,
115//! {
116//!   SqlPeople::default().drop(s).unwrap();
117//!   SqlPeople::default().create(s).unwrap();
118//!   SqlPeople::default().insert(s, &People { id: 1, name: "Jane Doe".to_string() }).unwrap();
119//!   SqlPeople::default().insert(s, &People { id: 2, name: "Jane Foe".to_string() }).unwrap();
120//!   SqlPeople::default().insert(s, &People { id: 3, name: "Jane Goe".to_string() }).unwrap();
121//!   let r: Vec<People> = SqlPeople::default().select(s).unwrap();
122//!   assert!(r.len() == 3);
123//!
124//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s, 
125//!     &filter::Or::from((Field::from("id").eq(1), Field::from("id").eq(3)))
126//!   ).unwrap();
127//!   assert!(r.len() == 2);
128//!
129//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s, 
130//!     &filter::And::from((Field::from("id").eq(1), Field::from("name").ne("Jane Doe")))
131//!   ).unwrap();
132//!   assert!(r.len() == 0);
133//!
134//!   let r: Vec<People> = SqlPeople::default().select_with_filter_order_limit_offset(s,
135//!     &filter::None::default(),
136//!     &Field::from("id").descending(),
137//!     1, // Limit
138//!     0, // Offset
139//!   ).unwrap();
140//!   assert!(r[0].id == 3);
141//!
142//!   SqlPeople::default().update_with_filter(s,
143//!     &Field::from("id").eq(3),
144//!     &People { id: 3, name: "Francis".to_string() },
145//!   ).unwrap();
146//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s,
147//!     &Field::from("id").eq(3),
148//!   ).unwrap();
149//!   assert!(r[0].name.eq("Francis"));
150//!
151//!   SqlPeople::default().delete_with_filter(s, &Field::from("name").eq("Francis")).unwrap();
152//!   let r: Vec<People> = SqlPeople::default().select(s).unwrap();
153//!   assert!(r.len() == 2);
154//!
155//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s, &Field::from("id").eq(1)).unwrap();
156//!   assert!(r.len() == 1);
157//!
158//!   r[0].name.clone()
159//! }
160//!
161//! // Called using a SQLite connection via rusqlite
162#![cfg_attr(not(feature="sqlite"), doc = "/*")]
163//! let mut conn = rusqlite::Connection::open_in_memory().unwrap();
164//! assert!(run(&mut conn).eq("Jane Doe"));
165#![cfg_attr(not(feature="sqlite"), doc = "*/")]
166//!
167//! // Call using a MySQL connection
168#![cfg_attr(not(feature="mysql"), doc = "/*")]
169//! let mut mysql_conn = mysql::Conn::new(
170//!   mysql::Opts::from_url("mysql://test@localhost/simpledb").unwrap()
171//! ).unwrap();
172//! assert!(run(&mut mysql_conn).eq("Jane Doe"));
173#![cfg_attr(not(feature="mysql"), doc = "*/")]
174//! ```
175//!
176//! This replicates the same test using the derive macro `DeriveSqlStatement`:
177//!
178//! ```rust
179//! use derive_sql::{DeriveSqlStatement};
180//! use derive_sql::traits::{Table, Insert, Delete, Update, SelectV2};
181//! use derive_sql::{traits, structs::filter, structs::Field, Result};
182//!
183//! // Define a `struct` representing the data to be stored
184//! #[derive(DeriveSqlStatement)]
185//! struct People {
186//!   id: i64,
187//!   name: String,
188//! }
189//!
190//! fn run<S, R>(s: &mut S) -> String
191//! where S: traits::Connection<R>,
192//!       R: traits::Row,
193//! {
194//!   SqlPeople::default().drop(s).unwrap();
195//!   SqlPeople::default().create(s).unwrap();
196//!   SqlPeople::default().insert(s, &People { id: 1, name: "Jane Doe".to_string() }).unwrap();
197//!   SqlPeople::default().insert(s, &People { id: 2, name: "Jane Foe".to_string() }).unwrap();
198//!   SqlPeople::default().insert(s, &People { id: 3, name: "Jane Goe".to_string() }).unwrap();
199//!   let r: Vec<People> = SqlPeople::default().select(s).unwrap();
200//!   assert!(r.len() == 3);
201//!
202//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s, 
203//!     &filter::Or::from((Field::from("id").eq(1), Field::from("id").eq(3)))
204//!   ).unwrap();
205//!   assert!(r.len() == 2);
206//!
207//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s, 
208//!     &filter::And::from((Field::from("id").eq(1), Field::from("name").ne("Jane Doe")))
209//!   ).unwrap();
210//!   assert!(r.len() == 0);
211//!
212//!   let r: Vec<People> = SqlPeople::default().select_with_filter_order_limit_offset(s,
213//!     &filter::None::default(),
214//!     &Field::from("id").descending(),
215//!     1, // Limit
216//!     0, // Offset
217//!   ).unwrap();
218//!   assert!(r[0].id == 3);
219//!
220//!   SqlPeople::default().update_with_filter(s,
221//!     &Field::from("id").eq(3),
222//!     &People { id: 3, name: "Francis".to_string() },
223//!   ).unwrap();
224//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s,
225//!     &Field::from("id").eq(3),
226//!   ).unwrap();
227//!   assert!(r[0].name.eq("Francis"));
228//!
229//!   SqlPeople::default().delete_with_filter(s, &Field::from("name").eq("Francis")).unwrap();
230//!   let r: Vec<People> = SqlPeople::default().select(s).unwrap();
231//!   assert!(r.len() == 2);
232//!
233//!   let r: Vec<People> = SqlPeople::default().select_with_filter(s, &Field::from("id").eq(1)).unwrap();
234//!   assert!(r.len() == 1);
235//!
236//!   r[0].name.clone()
237//! }
238//!
239//! // Called using a SQLite connection via rusqlite
240#![cfg_attr(not(feature="sqlite"), doc = "/*")]
241//! let mut conn = rusqlite::Connection::open_in_memory().unwrap();
242//! assert!(run(&mut conn).eq("Jane Doe"));
243#![cfg_attr(not(feature="sqlite"), doc = "*/")]
244//!
245//! // Call using a MySQL connection
246#![cfg_attr(not(feature="mysql"), doc = "/*")]
247//! let mut mysql_conn = mysql::Conn::new(
248//!   mysql::Opts::from_url("mysql://test@localhost/simpledb").unwrap()
249//! ).unwrap();
250//! assert!(run(&mut mysql_conn).eq("Jane Doe"));
251#![cfg_attr(not(feature="mysql"), doc = "*/")]
252//!
253//! // Call using a PostgreSQL connection
254#![cfg_attr(not(feature="postgres"), doc = "/*")]
255//!  let mut postgresql_conn = postgres::Client::configure()
256//!    .host("localhost")
257//!    .user("test")
258//!    .password("password")
259//!    .dbname("simpledb")
260//!    .connect(postgres::NoTls).unwrap();
261//! // assert!(run(&mut postgresql_conn).eq("Jane Doe"));
262#![cfg_attr(not(feature="postgres"), doc = "*/")]
263//! ```
264//!
265//! ## Legacy v0.10 feature:
266//!
267//! Available by activating feature `compability_v0_10`
268//!
269//! The trait `Sqlable` that defines a set of operation for 
270//! interacting with SQL tables:
271//! - `count` to provide a count of the number of items in the table.
272//! - `select` to return an array of the items in the table.
273//! - `insert` to insert a new item in the table.
274//! - `update` to update an existing item(s) with the values of the provided item.
275//! - `delete` to delete items in the table.
276//! - `delete_table` to drop the table.
277//!
278//! Implementation of the trait should allow the user of the trait to interact with the table via the above
279//! interface...
280//!
281//! The trait `Selectable` provides a possible interface for selector queries
282//! used in the `Sqlable` trait. It is a possible option - but not limited
283//! to it as the `Sqlable` trait uses an associated type for `Selector`.
284//!
285//! This crate includes:
286//! - the derive macro `DeriveSqlite` [when compiled with the feature `--features sqlite`]
287//! which provides an implementation of the `Sqlable` trait for SQLite as a wrapper around the `rusqlite`
288//! crate;
289//! - the derive macro `DeriveMysql` [when compiled with the feature `--features mysql`]
290//! which provides an implementation of the `Sqlable` trait for MySQL as a wrapper around the `mysql`
291//! crate;
292//!
293//! Please see examples here and the `DeriveSqlite` documentation.
294//!
295//! # Features:
296//! - `sqlite` provides a derive macro that implements the `Sqlable` trait for SQLite database (implemented as a wrapper around the `rusqlite` crate);
297//! - `mysql` provides a derive macro that implements the `Sqlable` trait for MySQL database (implemented as a wrapper around the `mysql` crate);
298//!
299//! # Mocking:
300//! The example of code below shows how the trait can be mocked using `mockall` for unit testing purposes. The
301//! example uses `mockall` external trait functionality - ie works in a code using this crate as a dependency.
302//! Note: one has to explicitely nominates the associated type in the method definitions.
303//!
304#![cfg_attr(feature="compatibility_v0_10", doc = "```rust")]
305#![cfg_attr(not(feature="compatibility_v0_10"), doc = "```ignore")]
306//! mockall::mock! {
307//!   SqlableStruct {}
308//!   impl derive_sql::Sqlable for SqlableStruct {
309//!     type Item = String;
310//!     type Error = Box<dyn std::error::Error>;
311//!     type Selector = ();
312//!     
313//!     fn count(&self, s: ()) -> Result<usize, Box<dyn std::error::Error>>;
314//!     fn select(&self, s: ()) -> Result<Vec<String>, Box<dyn std::error::Error>>;
315//!     fn insert(&mut self, item: String) -> Result<String, Box<dyn std::error::Error>>;
316//!     fn update(&mut self, s: (), item: String) -> Result<String, Box<dyn std::error::Error>>;
317//!     fn delete(&mut self, s: ()) -> Result<(), Box<dyn std::error::Error>>;
318//!     fn delete_table(&mut self) -> Result<(), Box<dyn std::error::Error>>;
319//!   }
320//! }
321//!
322//! fn my_function<S>(s: &mut S) -> Result<usize, Box<dyn std::error::Error>> 
323//! where S: derive_sql::Sqlable<Selector = (), Item = String, Error = Box<dyn std::error::Error>>,
324//! {
325//!   let _ = s.insert("an item".to_string())?;
326//!   Ok(s.count(())?)
327//! }
328//!
329//! // Create mock
330//! let mut mock = MockSqlableStruct::new();
331//! // Configure mock
332//! mock.expect_insert()
333//! .with(mockall::predicate::eq("an item".to_string()))
334//! .returning(|s| Ok(s));
335//! mock.expect_count().returning(|_| Ok(11));
336//!
337//! // Check result
338//! assert!(matches!(my_function(&mut mock), Ok(11)));
339//!
340//! ```
341//! 
342//!
343
344#[cfg(feature="sqlite")]
345/// Re-export `rusqlite` library used
346pub use rusqlite;
347
348#[cfg(feature="mysql")]
349/// Re-export `mysql` library used
350pub use mysql;
351
352#[cfg(feature="postgres")]
353/// Re-export 'postgres' library used
354pub use postgres;
355
356pub mod traits;
357pub mod proxy;
358pub mod structs; // pub use structs::{Field, filter, order};
359
360#[cfg(feature="compatibility_v0_10")]
361mod sqlable; 
362#[cfg(feature="compatibility_v0_10")]
363pub use sqlable::Sqlable;
364
365#[cfg(feature="compatibility_v0_10")]
366pub mod generics;
367
368#[cfg(feature="compatibility_v0_10")]
369mod selectable; 
370
371#[cfg(feature="compatibility_v0_10")]
372/// Implementation of generic approach to `WHERE` clauses filtering. Provides a generic operator for single clause and 
373/// `And` and `Or` clauses combinator;
374pub use selectable::filter;
375
376#[cfg(feature="compatibility_v0_10")]
377pub use selectable::Selectable;
378
379#[cfg(feature="compatibility_v0_10")]
380/// Convenient struct for implementing a simple filter, ie a struct that generates the content of a simple `WHERE a = value` clause
381pub use selectable::SimpleFilter;
382
383#[cfg(feature="compatibility_v0_10")]
384/// Convenient struct for implementing a limit, ie a struct that generates the content of a `LIMIT value` clause
385pub use selectable::SimpleLimit;
386
387#[cfg(feature="compatibility_v0_10")]
388/// Convenient struct for implementing an offset, ie a struct that generates the content of an `OFFSET value` clause
389pub use selectable::SimpleOffset;
390
391#[cfg(feature="compatibility_v0_10")]
392/// Convenient struct for implementing an order by, ie a struct that generates the content of an `ORDER BY value ASC|DESC` clause
393pub use selectable::{SimpleOrder, Order};
394
395#[cfg(all(feature="sqlite", feature="compatibility_v0_10"))]
396/// Derive macro to implement the `Sqlable` trait for a struct with named fields so that instances of the struct
397/// can be saved, queried, stored to/from an SQLite database. Uses `rusqlite`. Requires `--features sqlite`.
398pub use derive_sql_sqlite::DeriveSqlite;
399
400#[cfg(all(feature="mysql", feature="compatibility_v0_10"))]
401/// Derive macro to implement the `Sqlable` trait for a struct with named fields so that instances of the struct
402/// can be saved, queried, stored to/from a MySQL database. Uses `mysql`. Requires `--features mysql`.
403pub use derive_sql_mysql::DeriveMysql;
404
405pub use derive_sql_statement::DeriveSqlStatement;
406
407mod error;
408pub use error::{Result, DeriveSqlResult, Error};