-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
817 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,5 @@ | ||
[package] | ||
name = "naphtha" | ||
version = "0.0.0" | ||
authors = ["Lewin Probst <[email protected]>"] | ||
edition = "2018" | ||
license = "MIT OR Apache-2.0" | ||
description = "Work in progress" | ||
homepage = "https://github.com/emirror-de/naphtha" | ||
documentation = "https://github.com/emirror-de/naphtha" | ||
repository = "https://github.com/emirror-de/naphtha" | ||
readme = "README.md" | ||
keywords = [] | ||
categories = [] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
[workspace] | ||
members = [ | ||
"naphtha", | ||
"naphtha-proc-macro", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
[package] | ||
name = "naphtha-proc-macro" | ||
version = "0.1.0" | ||
authors = ["Lewin Probst <[email protected]>"] | ||
edition = "2018" | ||
license = "MIT OR Apache-2.0" | ||
description = "Supporting macro crate for naphtha" | ||
homepage = "https://github.com/emirror-de/naphtha" | ||
documentation = "https://github.com/emirror-de/naphtha" | ||
repository = "https://github.com/emirror-de/naphtha" | ||
readme = "README.md" | ||
keywords = ["database", "models", "connection", "migration"] | ||
categories = ["database"] | ||
|
||
[lib] | ||
proc-macro = true | ||
|
||
[features] | ||
default = [] | ||
sqlite = [] | ||
barrel-sqlite = [] | ||
|
||
[dependencies] | ||
syn = { version = "^1.0.74", features = ["parsing"] } | ||
proc-macro2 = "^1.0.28" | ||
quote = "^1.0.9" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# naphtha-proc-macro | ||
|
||
This crate is a support crate that contains the necessary macros for naphtha to | ||
compile. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#[cfg(any(feature = "barrel-sqlite", feature = "barrel-full"))] | ||
pub(crate) mod sqlite; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
use quote::quote; | ||
|
||
pub(crate) fn impl_sqlite() -> ::proc_macro2::TokenStream { | ||
quote! { | ||
impl ::naphtha::barrel::DatabaseSqlMigrationExecutor<::diesel::SqliteConnection, usize> for Person | ||
where | ||
Self: ::naphtha::barrel::DatabaseSqlMigration, | ||
{ | ||
fn execute_migration_up(conn: &::naphtha::DatabaseConnection<::diesel::SqliteConnection>) -> Result<usize, String> { | ||
use { | ||
::log::error, | ||
::naphtha::{barrel::Migration, DatabaseConnection}, | ||
crate::diesel::RunQueryDsl, | ||
}; | ||
let mut m = Migration::new(); | ||
Self::migration_up(&mut m); | ||
let m = m.make::<::naphtha::barrel::backend::Sqlite>(); | ||
|
||
let c = match conn.lock() { | ||
Ok(c) => c, | ||
Err(msg) => { | ||
error!("Could not aquire lock on DatabaseSqlMigrationExecutor::execute_migration_up: {}", msg.to_string()); | ||
return Err(msg.to_string()); | ||
} | ||
}; | ||
|
||
match ::diesel::sql_query(m).execute(&*c) { | ||
Ok(u) => Ok(u), | ||
Err(msg) => Err(msg.to_string()), | ||
} | ||
} | ||
|
||
fn execute_migration_down(conn: &::naphtha::DatabaseConnection<::diesel::SqliteConnection>) -> Result<usize, String> { | ||
use { | ||
::log::error, | ||
::naphtha::{barrel::Migration, DatabaseConnection}, | ||
crate::diesel::RunQueryDsl, | ||
}; | ||
let mut m = Migration::new(); | ||
Self::migration_down(&mut m); | ||
let m = m.make::<::naphtha::barrel::backend::Sqlite>(); | ||
|
||
let c = match conn.lock() { | ||
Ok(c) => c, | ||
Err(msg) => { | ||
error!("Could not aquire lock on DatabaseSqlMigrationExecutor::execute_migration_down for model: {}", msg.to_string()); | ||
return Err(msg.to_string()); | ||
} | ||
}; | ||
|
||
match ::diesel::sql_query(m).execute(&*c) { | ||
Ok(u) => Ok(u), | ||
Err(msg) => Err(msg.to_string()), | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#[cfg(feature = "sqlite")] | ||
pub(crate) mod sqlite; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
use { | ||
quote::quote, | ||
syn::{Data::Struct, DeriveInput}, | ||
}; | ||
|
||
pub(crate) fn impl_sqlite( | ||
ast: &DeriveInput, | ||
attr: &::proc_macro2::TokenStream, | ||
) -> ::proc_macro2::TokenStream { | ||
let database_modifier = impl_database_modifier(ast, attr); | ||
quote! { | ||
#database_modifier | ||
} | ||
} | ||
|
||
fn impl_database_modifier( | ||
ast: &DeriveInput, | ||
table_name: &::proc_macro2::TokenStream, | ||
) -> ::proc_macro2::TokenStream { | ||
let name = &ast.ident; | ||
let table_name = crate::helper::extract_table_name(table_name); | ||
//let table_name: syn::UsePath = syn::parse_quote! {#name::table_name()}; | ||
|
||
let insert_properties = generate_insert_properties(ast); | ||
if !crate::helper::has_id(ast) { | ||
panic!("No `id` member found in model `{}`. Currently only models having an `id` column of type `i32` are supported.", name); | ||
} | ||
|
||
/* | ||
let model_has_id_member = crate::helper::has_id(ast); | ||
if (#model_has_id_member) { | ||
#table_name.select(#table_name.primary_key()) | ||
.order(#table_name.primary_key().desc()) | ||
.first(&*c) | ||
} else { | ||
#table_name.select(#table_name.primary_key()) | ||
.filter(#table_name.primary_key().eq(self.primary_key())) | ||
.first(&*c) | ||
} | ||
*/ | ||
|
||
quote! { | ||
use { | ||
::diesel::{backend::Backend, prelude::*}, | ||
::log::error, | ||
::naphtha::{DatabaseModelModifier, DatabaseConnection}, | ||
}; | ||
impl DatabaseModelModifier<SqliteConnection> for #name | ||
where | ||
Self: ::naphtha::DatabaseUpdateHandler, | ||
{ | ||
fn insert(&mut self, conn: &DatabaseConnection<SqliteConnection>) -> bool { | ||
use { | ||
::naphtha::DatabaseModel, | ||
schema::{#table_name, #table_name::dsl::*}, | ||
}; | ||
// preventing duplicate insertion if default primary key gets | ||
// changed on database insertion. | ||
if self.primary_key() != Self::default_primary_key() { | ||
return false; | ||
} | ||
let c = match conn.lock() { | ||
Ok(c) => c, | ||
Err(msg) => { | ||
error!("Could not aquire lock on DatabaseModifier::insert for model:\n{:#?}", self); | ||
return false; | ||
} | ||
}; | ||
let res_id = match c.transaction::<_, ::diesel::result::Error, _>(|| { | ||
diesel::insert_into(#table_name) | ||
.values((#insert_properties)) | ||
.execute(&*c)?; | ||
#table_name.select(#table_name.primary_key()) | ||
.order(#table_name.primary_key().desc()) | ||
.first(&*c) | ||
}) { | ||
Ok(v) => v, | ||
Err(msg) => { | ||
error!("Failed inserting entity:\n{:#?}", self); | ||
return false; | ||
} | ||
}; | ||
self.set_primary_key(&res_id); | ||
true | ||
} | ||
|
||
fn update(&mut self, conn: &DatabaseConnection<SqliteConnection>) -> bool { | ||
let c = match conn.lock() { | ||
Ok(c) => c, | ||
Err(msg) => { | ||
error!("Could not aquire lock on DatabaseModifier::update for model:\n{:#?}", self); | ||
return false; | ||
} | ||
}; | ||
self.before_update(); | ||
let update_result = match self.save_changes::<Self>(&*c) { | ||
Ok(_) => true, | ||
Err(msg) => { | ||
error!("Failed updating entity:\n{:#?}", self); | ||
return false; | ||
}, | ||
}; | ||
self.after_update(); | ||
update_result | ||
} | ||
|
||
fn remove(self, conn: &DatabaseConnection<SqliteConnection>) -> bool { | ||
use { | ||
::log::info, | ||
::naphtha::DatabaseModel, | ||
schema::{#table_name, #table_name::dsl::*}, | ||
}; | ||
let c = match conn.lock() { | ||
Ok(c) => c, | ||
Err(msg) => { | ||
error!("Could not aquire lock on DatabaseModifier::remove for model:\n{:#?}", self); | ||
return false; | ||
} | ||
}; | ||
let num_deleted = ::diesel::delete( | ||
#table_name.filter( | ||
#table_name.primary_key().eq(self.primary_key()) | ||
) | ||
); | ||
match num_deleted.execute(&*c) { | ||
Ok(_) => { | ||
#[cfg(debug_assertions)] | ||
info!("Removed entity with primary key {} from database!", self.primary_key()); | ||
true | ||
}, | ||
Err(msg) => { | ||
error!("Could not aquire lock on DatabaseModifier::remove for model:\n{:#?}", self); | ||
false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn generate_insert_properties(ast: &DeriveInput) -> ::proc_macro2::TokenStream { | ||
let data = match &ast.data { | ||
Struct(data) => data, | ||
_ => panic!("Other data formats than \"struct\" is not supported yet!"), | ||
}; | ||
let mut collected_properties = quote! {}; | ||
for field in data.fields.iter() { | ||
if field.ident.is_none() { | ||
continue; | ||
} | ||
let fieldname = field.ident.as_ref().unwrap(); | ||
if &fieldname.to_string()[..] == "id" { | ||
// field id is currently used as primary key and therfore generated | ||
// by the database, so it must not be set during insertion. | ||
continue; | ||
} | ||
collected_properties = quote! { | ||
#collected_properties | ||
#fieldname.eq(&self.#fieldname), | ||
}; | ||
} | ||
collected_properties | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use syn::{Data::Struct, DeriveInput, Ident}; | ||
|
||
pub fn extract_table_name(attr: &::proc_macro2::TokenStream) -> Ident { | ||
for t in attr.clone().into_iter() { | ||
match t { | ||
::proc_macro2::TokenTree::Group(g) => { | ||
let mut is_next = false; | ||
for t in g.stream().into_iter() { | ||
match t { | ||
::proc_macro2::TokenTree::Ident(i) => { | ||
let ident = ::proc_macro2::Ident::new( | ||
"table_name", | ||
::proc_macro2::Span::call_site(), | ||
); | ||
is_next = ident == i; | ||
} | ||
::proc_macro2::TokenTree::Literal(l) => { | ||
if is_next { | ||
let l = l.to_string(); | ||
return Ident::new( | ||
&l[1..l.len() - 1], | ||
::proc_macro2::Span::call_site(), | ||
); | ||
} | ||
} | ||
_ => continue, | ||
} | ||
} | ||
} | ||
_ => continue, | ||
} | ||
} | ||
|
||
panic!("Attribute table_name has not been found in {}", attr); | ||
} | ||
|
||
pub fn has_id(ast: &DeriveInput) -> bool { | ||
let data = match &ast.data { | ||
Struct(data) => data, | ||
_ => { | ||
return false; | ||
} | ||
}; | ||
for field in data.fields.iter() { | ||
if field.ident.is_none() { | ||
continue; | ||
} | ||
let fieldname = field.ident.as_ref().unwrap(); | ||
match &fieldname.to_string()[..] { | ||
"id" => return true, | ||
_ => continue, | ||
}; | ||
} | ||
|
||
false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
extern crate proc_macro; | ||
extern crate quote; | ||
|
||
use { | ||
quote::quote, | ||
syn::{parse, DeriveInput}, | ||
}; | ||
|
||
#[cfg(any(feature = "barrel-full", feature = "barrel-sqlite",))] | ||
mod barrel_impl; | ||
mod database_impl; | ||
#[allow(dead_code)] | ||
mod helper; | ||
|
||
#[proc_macro_attribute] | ||
pub fn model( | ||
attr: ::proc_macro::TokenStream, | ||
item: ::proc_macro::TokenStream, | ||
) -> ::proc_macro::TokenStream { | ||
let ast: DeriveInput = parse(item).expect( | ||
"proc_macro_attribute model: Could not parse TokenStream input!", | ||
); | ||
let attr = format!("#[{}]", attr); | ||
let attr: ::proc_macro2::TokenStream = attr.parse().unwrap(); | ||
|
||
#[cfg(not(feature = "sqlite"))] | ||
let impl_sqlite = quote! {}; | ||
#[cfg(feature = "sqlite")] | ||
let impl_sqlite = database_impl::sqlite::impl_sqlite(&ast, &attr); | ||
|
||
#[cfg(not(any(feature = "barrel-full", feature = "barrel-sqlite")))] | ||
let impl_barrel_sqlite = quote! {}; | ||
#[cfg(any(feature = "barrel-full", feature = "barrel-sqlite"))] | ||
let impl_barrel_sqlite = barrel_impl::sqlite::impl_sqlite(); | ||
|
||
let output = quote! { | ||
use schema::*; | ||
#[derive(Debug, Queryable, Identifiable, AsChangeset, Associations)] | ||
#attr | ||
#ast | ||
|
||
#impl_sqlite | ||
#impl_barrel_sqlite | ||
}; | ||
|
||
::proc_macro::TokenStream::from(output) | ||
} |
Oops, something went wrong.