derive_sql_mysql/lib.rs
1//! Procedural macro to automatically generate an `Sqlable` trait for the
2//! provided struct that is compatible with an MySQL database connected using the `mysql` crate
3//! <https://crates.io/crates/mysql>.
4//!
5//! # How to use
6//!
7//! You write:
8//! ```rust
9//! # use derive_sql::*;
10//! # use derive_sql_mysql::DeriveMysql;
11//! #[derive(DeriveMysql)]
12//! pub struct Person {
13//! name: String,
14//! age: u32,
15//! }
16//! ```
17//!
18//! And you can use:
19//! ```rust
20//! # use derive_sql::*;
21//! # use derive_sql_mysql::DeriveMysql;
22//! # #[derive(DeriveMysql)]
23//! # pub struct Person {
24//! # name: String,
25//! # age: u32,
26//! # }
27//!
28//! let pool = ::mysql::Pool::new("mysql://test@localhost/simpledb").unwrap();
29//! let mut connection = pool.get_conn().unwrap();
30//! let mut db: PersonMysql<_> = derive_sql::proxy::mysql::Conn::from(connection).into();
31//!
32//! // initialise
33//! db.create_table().unwrap();
34//!
35//! // Insert entries
36//! db.insert(Person {name: "Abi".to_string(), age: 31 }).unwrap();
37//! db.insert(Person {name: "Bert".to_string(), age: 32 }).unwrap();
38//! db.insert(Person {name: "Charlie".to_string(), age: 33 }).unwrap();
39//!
40//! // Query
41//! let persons: Vec<Person> = db.select(Box::new(SimpleFilter::try_from(("age", 32)).unwrap())).unwrap();
42//! assert!(persons[0].name.eq("Bert"));
43//!
44//! // Update
45//! db.update(Box::new(SimpleFilter::try_from(("name", "Abi")).unwrap()), Person { name: "Abi".to_string(), age: 32 }).unwrap();
46//!
47//! // Delete
48//! db.delete(Box::new(SimpleFilter::try_from(("name", "Abi")).unwrap())).unwrap();
49//!
50//! // Clear the table
51//! db.delete_table().unwrap();
52//! ```
53//!
54//! # Container attributes:
55//! - `#[derive_sqlite(ident = ...)]` overwrite the name of the `rusqlite` wrapper from `{class}Mysql`;
56//! - `#[derive_sqlite(table_name = "...")]` specify the name of the table (default to the container name in lower case);
57//!
58//! # Field attributes:
59//! - `#[derive_sqlite(is_primary_key = true)]` nominate that one of the field is a primary key. Only one primary key can be specified.
60//! primary key fields are unique in the table. Primary key can NOT be a String - the following will not compile:
61//!
62//! ```compile_fail
63//! # use derive_sql::*;
64//! # use derive_sql_mysql::DeriveMysql;
65//! #[derive(DeriveMysql)]
66//! pub struct Person {
67//! #[derive_sqlite(is_primary_key = true)]
68//! name: String,
69//! age: u32,
70//! }
71//! ```
72//!
73//!
74//! - `#[derive_sqlite(on_insert = ...)]` nominate a function of the type `fn() -> {type}` with `{type}` corresponding to the type of the
75//! field. The function is called when the item is inserted and the value returned by the function is assigned to the field before the
76//! item is inserted. Typical use is to assign a creation date.
77//! - `#[derive_sqlite(on_update = ...)]` nominate a function of the type `fn() -> {type}` with `{type}` corresponding to the type of the
78//! field. The function is called when the item is updated and the value returned by the function is assigned to the field before the
79//! item is updated. Typical use is to assign a last modified date.
80//!
81
82mod mysql;
83
84use attribute_derive::{Attribute};
85
86#[derive(Attribute)]
87#[attribute(ident = derive_sqlite)]
88struct Attrs {
89 ident: Option<syn::Ident>,
90 table_name: Option<String>,
91}
92
93/*
94#[derive(Attribute)]
95#[attribute(ident = derive_sqlite)]
96struct FieldAttrs {
97 #[attribute(default = false)]
98 is_primary_key: bool,
99 on_insert: Option<syn::PatPath>,
100 on_update: Option<syn::PatPath>,
101}
102*/
103
104#[proc_macro_derive(DeriveMysql, attributes(derive_sqlite))]
105pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
106 syn::parse(input)
107 .and_then(|ast: syn::DeriveInput| {
108 Ok(mysql::Mysql::try_from(&ast)?.generate()?)
109 })
110 .unwrap_or_else(|e| e.into_compile_error())
111 .into()
112}
113