Skip to content

Commit

Permalink
FEAT: impl Increment
Browse files Browse the repository at this point in the history
  • Loading branch information
wuyuedefeng committed Sep 4, 2023
1 parent 375fbe9 commit 566621f
Show file tree
Hide file tree
Showing 12 changed files with 301 additions and 30 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,20 @@ let sql = User::query().paginate(1, 10).to_sql();

</details>

<details>
<summary>increment</summary>

```rust
let user: User = User::query().r#where("id", 1).fetch_one_as().await?;
let mut active_user: ArelActiveUser = user.into();
active_user.increment("lock_version", 5, |this, step| {
let value = this.lock_version.try_get_i32().unwrap_or(0) + step;
this.lock_version.set_unchanged(value);
}).await?;
```

</details>

<details>
<summary>transaction</summary>

Expand Down
156 changes: 128 additions & 28 deletions arel-macros/src/arel/arel_active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub(crate) fn create_arel_active_model(input: &crate::ItemInput) -> syn::Result<
let impl_trait_to_destroy_sql = impl_trait_to_destroy_sql(input)?;
let impl_trait_save_exec = impl_trait_save_exec(input)?;
let impl_trait_destroy_exec = impl_trait_destroy_exec(input)?;
let impl_trait_increment_exec = impl_trait_increment_exec(input)?;

let vis = input.vis()?;
ret_token_stream.extend(quote::quote!(
Expand All @@ -58,6 +59,7 @@ pub(crate) fn create_arel_active_model(input: &crate::ItemInput) -> syn::Result<
self.__persisted__
}
}

#impl_from_model
#impl_from_arel_model

Expand All @@ -73,9 +75,13 @@ pub(crate) fn create_arel_active_model(input: &crate::ItemInput) -> syn::Result<
self.save_exec(Self::Model::pool()?).await
}
#impl_trait_destroy_exec
async fn destroy(&mut self) -> anyhow::Result<arel::DatabaseQueryResult> {
async fn destroy(&mut self) -> arel::anyhow::Result<arel::DatabaseQueryResult> {
self.destroy_exec(Self::Model::pool()?).await
}
#impl_trait_increment_exec
async fn increment<K: ToString + Send, F: Send>(&mut self, key: K, step: i32, update_self_cb: F) -> arel::anyhow::Result<arel::DatabaseQueryResult> where F: FnOnce(&mut Self, i32) -> () {
self.increment_exec(key, step, update_self_cb, Self::Model::pool()?).await
}
}
));

Expand All @@ -84,7 +90,6 @@ pub(crate) fn create_arel_active_model(input: &crate::ItemInput) -> syn::Result<

fn impl_from_model(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_ident = input.ident()?;
let model_name_ident = struct_ident;
let fields = input.struct_fields()?;
let generics = input.generics()?;
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
Expand All @@ -106,8 +111,8 @@ fn impl_from_model(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenSt

let mut ret_token_stream = proc_macro2::TokenStream::new();
ret_token_stream.extend(quote::quote!(
impl #impl_generics From<#model_name_ident #type_generics> for ArelActiveUser #type_generics #where_clause {
fn from(value: #model_name_ident #type_generics) -> ArelActiveUser #type_generics {
impl #impl_generics From<#struct_ident #type_generics> for ArelActiveUser #type_generics #where_clause {
fn from(value: #struct_ident #type_generics) -> ArelActiveUser #type_generics {
let mut arel_active_model = Self::default();
#(#init_clauses)*
arel_active_model
Expand Down Expand Up @@ -165,8 +170,7 @@ fn impl_from_arel_model(input: &crate::ItemInput) -> syn::Result<proc_macro2::To
}

fn impl_trait_to_insert_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_ident = input.ident()?;
let model_ident = struct_ident;
// let struct_ident = input.ident()?;
let fields = input.struct_fields()?;

let mut insert_fields_init_clause = proc_macro2::TokenStream::new();
Expand Down Expand Up @@ -200,26 +204,26 @@ fn impl_trait_to_insert_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2
Ok(quote::quote!(
fn to_insert_sql(&self) -> arel::anyhow::Result<arel::Sql> {
let primary_keys = {
if let Some(keys) = #model_ident::primary_keys() {
if let Some(keys) = Self::Model::primary_keys() {
keys
} else if let Some(key) = #model_ident::primary_key() {
} else if let Some(key) = Self::Model::primary_key() {
vec![key]
} else {
vec![]
}
};
if primary_keys.len() == 0 {
return Err(anyhow::anyhow!("primary key/keys MUST SET"));
return Err(arel::anyhow::anyhow!("primary key/keys MUST SET"));
}

let table_name = #model_ident::table_name();
let table_name = Self::Model::table_name();
let mut final_sql = arel::Sql::new("");

let mut insert_fields: Vec<&'static str> = vec![];
let mut insert_values: Vec<arel::Value> = vec![];
#insert_fields_init_clause

if let Some(sql) = arel::statements::insert::Insert::<#model_ident>::new(insert_fields, insert_values).to_sql() {
if let Some(sql) = arel::statements::insert::Insert::<Self::Model>::new(insert_fields, insert_values).to_sql() {
final_sql = sql;
}
Ok(final_sql)
Expand All @@ -228,8 +232,7 @@ fn impl_trait_to_insert_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2
}

fn impl_trait_to_update_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_ident = input.ident()?;
let model_ident = struct_ident;
// let struct_ident = input.ident()?;
let fields = input.struct_fields()?;

let mut update_fields_init_clause = proc_macro2::TokenStream::new();
Expand Down Expand Up @@ -279,19 +282,19 @@ fn impl_trait_to_update_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2
Ok(quote::quote!(
fn to_update_sql(&self) -> arel::anyhow::Result<arel::Sql> {
let primary_keys = {
if let Some(keys) = #model_ident::primary_keys() {
if let Some(keys) = Self::Model::primary_keys() {
keys
} else if let Some(key) = #model_ident::primary_key() {
} else if let Some(key) = Self::Model::primary_key() {
vec![key]
} else {
vec![]
}
};
if primary_keys.len() == 0 {
return Err(anyhow::anyhow!("primary key/keys MUST SET"));
return Err(arel::anyhow::anyhow!("primary key/keys MUST SET"));
}

let table_name = #model_ident::table_name();
let table_name = Self::Model::table_name();
let mut final_sql = arel::Sql::new("");

let mut update_fields: Vec<&'static str> = vec![];
Expand All @@ -301,9 +304,9 @@ fn impl_trait_to_update_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2
#update_fields_init_clause

if update_where_fields.len() == 0 {
return Err(anyhow::anyhow!("Update where statement is blank!"));
return Err(arel::anyhow::anyhow!("Update where statement is blank!"));
}
if let Some(sql) = arel::statements::update::Update::<#model_ident>::new(update_fields, update_values, update_where_fields, update_where_values).to_sql() {
if let Some(sql) = arel::statements::update::Update::<Self::Model>::new(update_fields, update_values, update_where_fields, update_where_values).to_sql() {
final_sql = sql;
}

Expand All @@ -313,8 +316,7 @@ fn impl_trait_to_update_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2
}

fn impl_trait_to_destroy_sql(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> {
let struct_ident = input.ident()?;
let model_ident = struct_ident;
// let struct_ident = input.ident()?;
let fields = input.struct_fields()?;

let mut delete_fields_init_clause = proc_macro2::TokenStream::new();
Expand Down Expand Up @@ -362,33 +364,33 @@ fn impl_trait_to_destroy_sql(input: &crate::ItemInput) -> syn::Result<proc_macro
Ok(quote::quote!(
fn to_destroy_sql(&self) -> arel::anyhow::Result<arel::Sql> {
let primary_keys = {
if let Some(keys) = #model_ident::primary_keys() {
if let Some(keys) = Self::Model::primary_keys() {
keys
} else if let Some(key) = #model_ident::primary_key() {
} else if let Some(key) = Self::Model::primary_key() {
vec![key]
} else {
vec![]
}
};
if primary_keys.len() == 0 {
return Err(anyhow::anyhow!("Primary key/keys MUST SET"));
return Err(arel::anyhow::anyhow!("Primary key/keys MUST SET"));
}

let table_name = #model_ident::table_name();
let table_name = Self::Model::table_name();
let mut final_sql = arel::Sql::new("");
if self.__persisted__ {
let mut delete_where_fields: Vec<&'static str> = vec![];
let mut delete_where_values: Vec<arel::Value> = vec![];
#delete_fields_init_clause

if delete_where_fields.len() == 0 {
return Err(anyhow::anyhow!("Update where statement is blank!"));
return Err(arel::anyhow::anyhow!("Update where statement is blank!"));
}
if let Some(sql) = arel::statements::delete::Delete::<#model_ident>::new(delete_where_fields, delete_where_values).to_sql() {
if let Some(sql) = arel::statements::delete::Delete::<Self::Model>::new(delete_where_fields, delete_where_values).to_sql() {
final_sql = sql;
}
} else {
return Err(anyhow::anyhow!("Model is Not Persisted"));
return Err(arel::anyhow::anyhow!("Model is Not Persisted"));
}
Ok(final_sql)
}
Expand Down Expand Up @@ -463,3 +465,101 @@ fn impl_trait_destroy_exec(input: &crate::ItemInput) -> syn::Result<proc_macro2:
}
))
}

fn impl_trait_increment_exec(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> {
// let struct_ident = input.ident()?;
let fields = input.struct_fields()?;

let mut update_fields_init_clause = proc_macro2::TokenStream::new();
for field in fields.iter() {
let ident = &field.ident;
// let r#type = &field.ty;
let field_name = {
if let Some((rename, _)) = crate::ItemInput::get_field_path_value(field, vec!["arel"], "rename", None)? {
rename
} else {
match ident {
Some(ident) => ident.to_string().trim_start_matches("r#").to_string(),
_ => return Err(syn::Error::new_spanned(field, "Field name can not Blank!")),
}
}
};
update_fields_init_clause.extend(quote::quote!(
let field_name = #field_name;
match &self.#ident {
arel::ActiveValue::Changed(nv, ov) => {
if primary_keys.contains(&field_name.into()) {
match ov.as_ref() {
arel::ActiveValue::Unchanged(v) => {
update_where_fields.push(field_name);
update_where_values.push(v.into());
},
_ => ()
}

}
},
arel::ActiveValue::Unchanged(v) => {
if primary_keys.contains(&field_name.into()) {
update_where_fields.push(field_name);
update_where_values.push(v.into());
}
}
_ => ()
}
));
}

Ok(quote::quote!(
async fn increment_exec<'a, K: ToString + Send, F: Send, E>(&mut self, key: K, step: i32, update_self_cb: F, executor: E) -> arel::anyhow::Result<arel::DatabaseQueryResult>
where
F: FnOnce(&mut Self, i32) -> (),
E: arel::sqlx::Executor<'a, Database = arel::Database>,
{
let key = key.to_string();
let primary_keys = {
if let Some(keys) = Self::Model::primary_keys() {
keys
} else if let Some(key) = Self::Model::primary_key() {
vec![key]
} else {
vec![]
}
};
if primary_keys.len() == 0 {
return Err(arel::anyhow::anyhow!("primary key/keys MUST SET"));
}
let column_name = {
if let Some(column_name) = Self::Model::table_column_name(&key) {
column_name.to_string()
} else {
return Err(arel::anyhow::anyhow!("Key: {}, Column name Can not found!", key));
}
};


let mut update_where_fields: Vec<&'static str> = vec![];
let mut update_where_values: Vec<arel::Value> = vec![];
#update_fields_init_clause

let increment_statement = arel::statements::increment::Increment::<Self::Model>::new(column_name, step, update_where_fields, update_where_values);
if let Some(increment_sql) =increment_statement.to_sql() {
if self.__persisted__ {
match increment_sql.exec(executor).await {
Ok(val) => {
if val.rows_affected() >= 1 {
update_self_cb(self, step);
}
Ok(val)
}
Err(err) => Err(err),
}
} else {
return Err(arel::anyhow::anyhow!("Model is Not Persisted"));
}
} else {
return Err(arel::anyhow::anyhow!("Increment sql error!"));
}
}
))
}
31 changes: 31 additions & 0 deletions arel-macros/src/arel/arel_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,34 @@ pub(crate) fn impl_primary_key_or_primary_keys(input: &crate::ItemInput) -> syn:

Ok(ret_token_stream)
}

pub(crate) fn impl_table_column_name(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream> {
let fields = input.struct_fields()?;

let mut get_column_name_clauses = vec![];
for field in fields.iter() {
let ident = &field.ident;

let field_name = {
if let Some((rename, _)) = crate::ItemInput::get_field_path_value(field, vec!["arel"], "rename", None)? {
rename
} else {
match ident {
Some(ident) => ident.to_string().trim_start_matches("r#").to_string(),
_ => return Err(syn::Error::new_spanned(field, "Field name can not Blank!")),
}
}
};

get_column_name_clauses.push(quote::quote!(if struct_key.as_ref() == stringify!(#ident) {
return std::option::Option::Some(std::borrow::Cow::Borrowed(#field_name))
}));
}

Ok(quote::quote!(
fn _table_column_name<K: AsRef<str>>(struct_key: K) -> Option<std::borrow::Cow<'static, str>> {
#(#get_column_name_clauses)*
None
}
))
}
2 changes: 2 additions & 0 deletions arel-macros/src/arel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ fn do_expand(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream>

let arel_trait_impl_table_name = arel_trait::impl_table_name(input)?;
let arel_trait_impl_primary_key_or_primary_keys = arel_trait::impl_primary_key_or_primary_keys(input)?;
let impl_table_column_name = arel_trait::impl_table_column_name(input)?;
let model_impl_trait_sqlx_from_row = impl_trait_sqlx_from_row(input)?;

let generics = input.generics()?;
Expand All @@ -61,6 +62,7 @@ fn do_expand(input: &crate::ItemInput) -> syn::Result<proc_macro2::TokenStream>
impl #impl_generics arel::SuperArel for #model_name_ident #type_generics #where_clause {
#arel_trait_impl_table_name
#arel_trait_impl_primary_key_or_primary_keys
#impl_table_column_name
}
impl #impl_generics arel::traits::ArelPersisted for #model_name_ident #type_generics #where_clause {
fn set_persisted(&mut self, persisted: bool) {
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ impl std::fmt::Display for JoinType {
pub fn Set<V, UV: Into<V>>(uv: UV) -> ActiveValue<V>
where
V: Into<Value> + Clone + PartialEq,
UV: Into<V>,
{
ActiveValue::Changed(uv.into(), Box::new(ActiveValue::NotSet))
}

0 comments on commit 566621f

Please sign in to comment.