mirror of
https://github.com/diesel-rs/diesel.git
synced 2024-10-04 01:28:13 +03:00
Implement AsChangeset
using a stable procedural macro
This is slightly more complicated that the previous macros, as we have the additional `treat_none_as_null` option. This should behave identically to the procedural macro
This commit is contained in:
parent
f054710da1
commit
d9cabcc230
@ -19,6 +19,10 @@ for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/
|
||||
those wishing to avoid syntax extensions from `diesel_codegen`. See
|
||||
http://docs.diesel.rs/diesel/macro.Identifiable!.html for details.
|
||||
|
||||
* The `AsChangeset!` macro can now be used instead of `#[changeset_for(table)]`
|
||||
for those wishing to avoid syntax extensions from `diesel_codegen`. See
|
||||
http://docs.diesel.rs/diesel/macro.AsChangeset!.html for details.
|
||||
|
||||
* Added support for the PostgreSQL `ALL` operator. See
|
||||
http://docs.diesel.rs/diesel/pg/expression/dsl/fn.all.html for details.
|
||||
|
||||
|
266
diesel/src/macros/as_changeset.rs
Normal file
266
diesel/src/macros/as_changeset.rs
Normal file
@ -0,0 +1,266 @@
|
||||
#[macro_export]
|
||||
macro_rules! AsChangeset {
|
||||
// Provide a default value for treat_none_as_null if not provided
|
||||
(
|
||||
($table_name:ident)
|
||||
$($body:tt)*
|
||||
) => {
|
||||
AsChangeset! {
|
||||
($table_name, treat_none_as_null="false")
|
||||
$($body)*
|
||||
}
|
||||
};
|
||||
|
||||
// Strip meta items, pub (if present) and struct from definition
|
||||
(
|
||||
$args:tt
|
||||
$(#[$ignore:meta])*
|
||||
$(pub)* struct $($body:tt)*
|
||||
) => {
|
||||
AsChangeset! {
|
||||
$args
|
||||
$($body)*
|
||||
}
|
||||
};
|
||||
|
||||
// Handle struct with lifetimes
|
||||
(
|
||||
($table_name:ident, treat_none_as_null=$treat_none_as_null:tt)
|
||||
$struct_name:ident <$($lifetime:tt),*>
|
||||
$body:tt $(;)*
|
||||
) => {
|
||||
__diesel_parse_struct_body! {
|
||||
(
|
||||
struct_name = $struct_name,
|
||||
table_name = $table_name,
|
||||
treat_none_as_null = $treat_none_as_null,
|
||||
struct_ty = $struct_name<$($lifetime),*>,
|
||||
lifetimes = ($($lifetime),*),
|
||||
),
|
||||
callback = AsChangeset,
|
||||
body = $body,
|
||||
}
|
||||
};
|
||||
|
||||
// Handle struct with no lifetimes. We pass a dummy lifetime to reduce
|
||||
// the amount of branching later.
|
||||
(
|
||||
($table_name:ident, treat_none_as_null=$treat_none_as_null:tt)
|
||||
$struct_name:ident
|
||||
$body:tt $(;)*
|
||||
) => {
|
||||
__diesel_parse_struct_body! {
|
||||
(
|
||||
struct_name = $struct_name,
|
||||
table_name = $table_name,
|
||||
treat_none_as_null = $treat_none_as_null,
|
||||
struct_ty = $struct_name,
|
||||
lifetimes = ('a),
|
||||
),
|
||||
callback = AsChangeset,
|
||||
body = $body,
|
||||
}
|
||||
};
|
||||
|
||||
// Receive parsed fields of struct from `__diesel_parse_struct_body`
|
||||
(
|
||||
(
|
||||
struct_name = $struct_name:ident,
|
||||
table_name = $table_name:ident,
|
||||
treat_none_as_null = $treat_none_as_null:tt,
|
||||
$($headers:tt)*
|
||||
),
|
||||
fields = $fields:tt,
|
||||
) => {
|
||||
AsChangeset_construct_changeset_ty! {
|
||||
(
|
||||
fields = $fields,
|
||||
struct_name = $struct_name,
|
||||
table_name = $table_name,
|
||||
treat_none_as_null = $treat_none_as_null,
|
||||
$($headers)*
|
||||
),
|
||||
table_name = $table_name,
|
||||
treat_none_as_null = $treat_none_as_null,
|
||||
fields = $fields,
|
||||
changeset_ty = (),
|
||||
}
|
||||
};
|
||||
|
||||
// Receive changeset ty when tuple struct from `AsChangeset_construct_changeset_ty`
|
||||
(
|
||||
(
|
||||
fields = [$({
|
||||
column_name: $column_name:ident,
|
||||
field_ty: $field_ty:ty,
|
||||
field_kind: $field_kind:ident,
|
||||
})+],
|
||||
struct_name = $struct_name:ident,
|
||||
$($headers:tt)*
|
||||
),
|
||||
changeset_ty = $changeset_ty:ty,
|
||||
) => {
|
||||
AsChangeset! {
|
||||
$($headers)*
|
||||
self_to_columns = $struct_name($(ref $column_name),+),
|
||||
columns = ($($column_name, $field_kind),+),
|
||||
field_names = [],
|
||||
changeset_ty = $changeset_ty,
|
||||
}
|
||||
};
|
||||
|
||||
// Receive changeset ty when named struct from `AsChangeset_construct_changeset_ty`
|
||||
(
|
||||
(
|
||||
fields = [$({
|
||||
field_name: $field_name:ident,
|
||||
column_name: $column_name:ident,
|
||||
field_ty: $field_ty:ty,
|
||||
field_kind: $field_kind:ident,
|
||||
})+],
|
||||
struct_name = $struct_name:ident,
|
||||
$($headers:tt)*
|
||||
),
|
||||
changeset_ty = $changeset_ty:ty,
|
||||
) => {
|
||||
AsChangeset! {
|
||||
$($headers)*
|
||||
self_to_columns = $struct_name { $($field_name: ref $column_name),+ },
|
||||
columns = ($($column_name, $field_kind),+),
|
||||
field_names = [$($field_name)+],
|
||||
changeset_ty = $changeset_ty,
|
||||
}
|
||||
};
|
||||
|
||||
// Construct final impl
|
||||
(
|
||||
table_name = $table_name:ident,
|
||||
treat_none_as_null = $treat_none_as_null:tt,
|
||||
struct_ty = $struct_ty:ty,
|
||||
lifetimes = ($($lifetime:tt),*),
|
||||
self_to_columns = $self_to_columns:pat,
|
||||
columns = ($($column_name:ident, $field_kind:ident),+),
|
||||
field_names = $field_names:tt,
|
||||
changeset_ty = $changeset_ty:ty,
|
||||
) => {
|
||||
__diesel_parse_as_item! {
|
||||
impl<$($lifetime: 'update,)* 'update> $crate::query_builder::AsChangeset
|
||||
for &'update $struct_ty
|
||||
{
|
||||
type Target = $table_name::table;
|
||||
type Changeset = $changeset_ty;
|
||||
|
||||
#[allow(non_shorthand_field_patterns)]
|
||||
fn as_changeset(self) -> Self::Changeset {
|
||||
let $self_to_columns = *self;
|
||||
($(
|
||||
AsChangeset_column_expr!(
|
||||
$table_name::$column_name,
|
||||
$column_name,
|
||||
none_as_null = $treat_none_as_null,
|
||||
field_kind = $field_kind,
|
||||
)
|
||||
,)+)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! AsChangeset_construct_changeset_ty {
|
||||
// Handle option field when treat none as null is false
|
||||
(
|
||||
$headers:tt,
|
||||
table_name = $table_name:ident,
|
||||
treat_none_as_null = "false",
|
||||
fields = [{
|
||||
$(field_name: $field_name:ident,)*
|
||||
column_name: $column_name:ident,
|
||||
field_ty: Option<$field_ty:ty>,
|
||||
field_kind: option,
|
||||
} $($tail:tt)*],
|
||||
changeset_ty = ($($changeset_ty:ty,)*),
|
||||
) => {
|
||||
AsChangeset_construct_changeset_ty! {
|
||||
$headers,
|
||||
table_name = $table_name,
|
||||
treat_none_as_null = "false",
|
||||
fields = [$($tail)*],
|
||||
changeset_ty = ($($changeset_ty,)*
|
||||
Option<$crate::expression::predicates::Eq<
|
||||
$table_name::$column_name,
|
||||
$crate::expression::bound::Bound<
|
||||
<$table_name::$column_name as $crate::expression::Expression>::SqlType,
|
||||
&'update $field_ty,
|
||||
>,
|
||||
>>,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
// Handle normal field or option when treat none as null is true
|
||||
(
|
||||
$headers:tt,
|
||||
table_name = $table_name:ident,
|
||||
treat_none_as_null = $treat_none_as_null:tt,
|
||||
fields = [{
|
||||
$(field_name: $field_name:ident,)*
|
||||
column_name: $column_name:ident,
|
||||
field_ty: $field_ty:ty,
|
||||
$($ignore:tt)*
|
||||
} $($tail:tt)*],
|
||||
changeset_ty = ($($changeset_ty:ty,)*),
|
||||
) => {
|
||||
AsChangeset_construct_changeset_ty! {
|
||||
$headers,
|
||||
table_name = $table_name,
|
||||
treat_none_as_null = $treat_none_as_null,
|
||||
fields = [$($tail)*],
|
||||
changeset_ty = ($($changeset_ty,)*
|
||||
$crate::expression::predicates::Eq<
|
||||
$table_name::$column_name,
|
||||
$crate::expression::bound::Bound<
|
||||
<$table_name::$column_name as $crate::expression::Expression>::SqlType,
|
||||
&'update $field_ty,
|
||||
>,
|
||||
>,
|
||||
),
|
||||
}
|
||||
};
|
||||
|
||||
// Finished parsing
|
||||
(
|
||||
$headers:tt,
|
||||
table_name = $table_name:ident,
|
||||
treat_none_as_null = $treat_none_as_null:tt,
|
||||
fields = [],
|
||||
changeset_ty = $changeset_ty:ty,
|
||||
) => {
|
||||
AsChangeset!($headers, changeset_ty = $changeset_ty,);
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! AsChangeset_column_expr {
|
||||
// When none_as_null is false, we don't update fields which aren't present
|
||||
(
|
||||
$column:expr,
|
||||
$field_access:expr,
|
||||
none_as_null = "false",
|
||||
field_kind = option,
|
||||
) => {
|
||||
$field_access.as_ref().map(|f| $column.eq(f))
|
||||
};
|
||||
|
||||
// If none_as_null is true, or the field kind isn't option, assign blindly
|
||||
(
|
||||
$column:expr,
|
||||
$field_access:expr,
|
||||
$($args:tt)*
|
||||
) => {
|
||||
$column.eq($field_access)
|
||||
};
|
||||
}
|
@ -487,6 +487,7 @@ macro_rules! print_sql {
|
||||
#[macro_use] mod parse;
|
||||
#[macro_use] mod query_id;
|
||||
|
||||
#[macro_use] mod as_changeset;
|
||||
#[macro_use] mod associations;
|
||||
#[macro_use] mod identifiable;
|
||||
#[macro_use] mod insertable;
|
||||
|
Loading…
Reference in New Issue
Block a user