derive_sql_sqlite/
sqlite.rs1use super::*;
2use derive_sql_common::derive::fields;
3
4pub struct Sqlite<'a> {
5 ast: &'a syn::DeriveInput,
6 fields_named: &'a syn::FieldsNamed,
7}
8
9impl<'a> TryFrom<&'a syn::DeriveInput> for Sqlite<'a> {
10 type Error = syn::parse::Error;
11 fn try_from(ast: &'a syn::DeriveInput) -> syn::parse::Result<Sqlite> {
12 if let syn::Data::Struct(syn::DataStruct { fields: syn::Fields::Named(fields_named), .. }) = &ast.data {
13 Ok(Sqlite { ast, fields_named })
14 } else {
15 Err(syn::Error::new(ast.ident.span(), "Procedural macro DeriveSqlite is intended to be applied to struct with named fields."))
16 }
17 }
18}
19
20impl<'a> Sqlite<'a> {
21 pub fn generate(self) -> syn::parse::Result<proc_macro2::TokenStream> {
22 let attrs = Attrs::from_attributes(&self.ast.attrs)?;
23 let vis = &self.ast.vis;
24 let ident = &self.ast.ident;
25 let sqlite_ident = attrs.ident.as_ref().map(|i| i.clone()).unwrap_or_else(|| quote::format_ident!("{ident}Sqlite"));
26 let table_name = attrs.table_name.as_ref().map(|i| i.clone()).unwrap_or_else(|| format!("{ident}").to_lowercase());
27
28 let fields = self.fields_named.named.iter()
29 .map(|f| f.try_into().map_err(|e| syn::Error::new(ident.span(), format!("{e}"))))
30 .collect::<std::result::Result<Vec<fields::Fields>, syn::Error>>()?;
31
32 let declaration = {
33 let doc = format!("Wrapper struct to query item of type `{ident}` from SQLite database using `rusqlite` library");
34 quote::quote! {
35 #[doc = #doc]
36 #vis struct #sqlite_ident <T>
37 where T: derive_sql::proxy::sqlite::SqliteTrait
38 {
39 conn: T,
40 }
41 }
42 };
43
44 let from_rusqlite_impl = {
45 let doc = format!("Create a new instance from a `rusqlite` connection");
46 quote::quote! {
47 impl std::convert::From<rusqlite::Connection> for #sqlite_ident <derive_sql::proxy::sqlite::Conn>
48 {
49 #[doc = #doc]
50 fn from(v: rusqlite::Connection) -> Self { #sqlite_ident { conn: derive_sql::proxy::sqlite::Conn::from(v) } }
51 }
52 }
53 };
54
55 let from_sqlite_trait_impl = {
56 let doc = format!("Create a new instance from a connection implementing `SqliteTrait`");
57 quote::quote! {
58 impl<T> std::convert::From<T> for #sqlite_ident <T>
59 where T: derive_sql::proxy::sqlite::SqliteTrait
60 {
61 #[doc = #doc]
62 fn from(conn: T) -> Self { #sqlite_ident { conn } }
63 }
64 }
65 };
66
67 let static_members = {
68 let members = fields.iter().map(|f| f.as_pub_static_member()).collect::<Vec<proc_macro2::TokenStream>>();
69 quote::quote! {
70 pub const TABLE_NAME: &'static str = #table_name ;
71 #( #members )*
72 }
73 };
74
75 let create_table = {
76 let doc = format!("Create table `{table_name}` in the SQLite database");
77 let statement = format!("CREATE TABLE IF NOT EXISTS {table_name} ( {} )",
78 fields.iter()
79 .map(|f| {
80 let ident = f.ident();
81 let sql_type = f.sql_type().to_string();
82 match (f.is_primary_key(), f.is_unique()) {
83 (true, true) => Ok(format!("{ident} {sql_type} PRIMARY KEY UNIQUE")),
84 (false, true) => Ok(format!("{ident} {sql_type} UNIQUE")),
85 (true, false) => Ok(format!("{ident} {sql_type} PRIMARY KEY")),
86 (false, false) => Ok(format!("{ident} {sql_type}")),
87 }
88 })
89 .collect::<syn::parse::Result<Vec<String>>>()?
90 .join(", ")
91 );
92 let doc = format!("{doc}<br>SQL statement: `{statement}`");
93 quote::quote! {
94 #[doc = #doc]
95 pub fn create_table(&mut self) -> std::result::Result<(), Box<dyn std::error::Error>> {
96 let stmt = format!("{}", #statement);
97 self.conn.execute(stmt.as_str(), ())?;
98 Ok(())
99 }
100 }
101 };
102
103 let count = {
104 let doc = format!("Implementation of functionality to count the number of item(s) from database table `{table_name}`");
105 let statement = format!("SELECT COUNT(*) FROM {table_name}");
106 let doc = format!("{doc}<br>SQL statement: `{statement}`");
107 quote::quote! {
108 #[doc = #doc]
109 fn count(&self, select: Self::Selector) -> std::result::Result<usize, Self::Error> {
110 let stmt = format!("{} {}", #statement, select.statement());
111 let r = self.conn.query_first(stmt.as_str(), [], |r| r.get(0))?;
112 Ok(r)
113 }
114 }
115 };
116
117 let select = {
118 let doc = format!("Retrieve a list of `{ident}` items matching the selector parameter from SQLite database table `{table_name}`");
119 let statement = format!("SELECT {} FROM {table_name}",
120 fields.iter().map(|f| f.name()).collect::<Vec<String>>().join(", ")
121 );
122 let doc = format!("{doc}<br>SQL statement: `{statement}`");
123 let fields = fields.iter().map(|f| f.ident()).collect::<Vec<&syn::Ident>>();
124 let assignements = fields.iter().enumerate().map(|(i, _)| quote::quote! { r.get(#i)? } ).collect::<Vec<proc_macro2::TokenStream>>();
125 quote::quote! {
126 #[doc = #doc]
127 fn select(&self, select: Self::Selector) -> std::result::Result<Vec<Self::Item>, Self::Error> {
128 let stmt = format!("{} {}", #statement, select.statement());
129 let r = self.conn.query_map(stmt.as_str(), [], |r| Ok( #ident { #( #fields: #assignements ),* } ))?;
130 Ok(r)
131 }
132 }
133 };
134
135 let insert = {
136 let doc = format!("Insert an item {ident} into the SQLite database table {table_name}");
137 let functions = fields.iter()
138 .filter_map(|f| {
139 let ident = f.ident();
140 f.on_insert().as_ref().map(|p| quote::quote! { item.#ident = #p(); })
141 })
142 .collect::<Vec<proc_macro2::TokenStream>>();
143 let statement = format!("INSERT INTO {table_name} ({}) VALUES ({})",
144 fields.iter().map(|f| f.name()).collect::<Vec<String>>().join(", "),
145 fields.iter().enumerate().map(|(i,_)| format!("?{}", i+1)).collect::<Vec<String>>().join(", ")
146 );
147 let doc = format!("{doc}<br>SQL statement: `{statement}`");
148 let params = fields.iter().map(|f| f.ident()).collect::<Vec<&syn::Ident>>();
149 quote::quote! {
150 #[doc = #doc]
151 fn insert(&mut self, mut item: Self::Item) -> std::result::Result<Self::Item, Self::Error> {
152 #( #functions )*
153 let stmt = format!("{}", #statement);
154 self.conn.execute(stmt.as_str(), ( #( &item.#params ),* ))?;
155 Ok(item)
156 }
157 }
158 };
159
160 let update = {
161 let doc = format!("Update item(s) nominated by the selector in the SQLite table {table_name}");
162 let functions = fields.iter()
163 .filter_map(|f| {
164 let ident = f.ident();
165 f.on_update().as_ref().map(|p| quote::quote! { item.#ident = #p(); })
166 })
167 .collect::<Vec<proc_macro2::TokenStream>>();
168 let statement = format!("UPDATE {table_name} SET {}",
169 fields.iter().enumerate()
170 .map(|(i,f)| format!("{} = ?{}", f.ident(), i+1))
171 .collect::<Vec<String>>().join(", ")
172 );
173 let doc = format!("{doc}<br>SQL statement: `{statement}`");
174 let params = fields.iter().map(|f| f.ident()).collect::<Vec<&syn::Ident>>();
175
176 quote::quote! {
177 #[doc = #doc]
178 fn update(&mut self, select: Self::Selector, mut item: Self::Item) -> std::result::Result<Self::Item, Self::Error> {
179 #( #functions )*
180 let stmt = format!("{} {}", #statement, select.statement());
181 self.conn.execute(stmt.as_str(), ( #( &item.#params ),* ))?;
182 Ok(item)
183 }
184 }
185 };
186
187 let delete = {
188 let doc = format!("Implementation of functionality to delete item(s) from database table `{table_name}`");
189 let statement = format!("DELETE FROM {table_name}");
190 let doc = format!("{doc}<br>SQL statement: `{statement}`");
191 quote::quote! {
192 #[doc = #doc]
193 fn delete(&mut self, select: Self::Selector) -> std::result::Result<(), Self::Error> {
194 let stmt = format!("{} {}", #statement, select.statement());
195 self.conn.execute(stmt.as_str(), ())?;
196 Ok(())
197 }
198 }
199 };
200
201 let delete_table = {
202 let doc = format!("Delete table `{table_name}` from SQLite database");
203 let statement = format!("DROP TABLE {table_name}");
204 let doc = format!("{doc}<br>SQL statement: `{statement}`");
205 quote::quote! {
206 #[doc = #doc]
207 fn delete_table(&mut self) -> std::result::Result<(), Self::Error> {
208 self.conn.execute(#statement, ())?;
209 Ok(())
210 }
211 }
212 };
213
214 Ok(quote::quote! {
215 #declaration
216 #from_rusqlite_impl
217 #from_sqlite_trait_impl
218
219 impl<T> #sqlite_ident <T>
220 where T: derive_sql::proxy::sqlite::SqliteTrait
221 {
222 #static_members
223 #create_table
224 }
225
226 impl<T> derive_sql::Sqlable for #sqlite_ident <T>
227 where T: derive_sql::proxy::sqlite::SqliteTrait
228 {
229 type Item = #ident;
230 type Error = Box<dyn std::error::Error>;
231 type Selector = Box<dyn derive_sql::Selectable>;
232 #count
233 #select
234 #insert
235 #update
236 #delete
237 #delete_table
238 }
239 })
240 }
241}
242