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};