derive_sql_sqlite/
sqlite.rs

1use 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