Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement "Nest" for DerivePartialModel and From QueryResult #1716

Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 33 additions & 11 deletions sea-orm-macros/src/derives/from_query_result.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,34 @@
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::{ext::IdentExt, Data, DataStruct, Field, Fields, Generics};
use syn::{ext::IdentExt, Data, DataStruct, Field, Fields, Generics, Type};

use super::util::field_attr_contain_key;

struct FieldInfo {
ident: Ident,
flatten: bool,
ty: Type,
}

impl From<Field> for FieldInfo {
fn from(value: Field) -> Self {
Self {
flatten: field_attr_contain_key(&value, "flatten"),
ident: format_ident!("{}", value.ident.unwrap().to_string()),
ty: value.ty,
}
}
}

/// Method to derive a [QueryResult](sea_orm::QueryResult)
pub fn expand_derive_from_query_result(
ident: Ident,
data: Data,
generics: Generics,
) -> syn::Result<TokenStream> {
let arg_row = &format_ident!("row");
let arg_pre = &format_ident!("pre");

let fields = match data {
Data::Struct(DataStruct {
fields: Fields::Named(named),
Expand All @@ -20,16 +41,17 @@ pub fn expand_derive_from_query_result(
}
};

let field: Vec<Ident> = fields
.into_iter()
.map(|Field { ident, .. }| format_ident!("{}", ident.unwrap().to_string()))
.collect();
let field: Vec<FieldInfo> = fields.into_iter().map(FieldInfo::from).collect();

let name: Vec<TokenStream> = field
let field_query: Vec<TokenStream> = field
.iter()
.map(|f| {
let s = f.unraw().to_string();
quote! { #s }
.map(|FieldInfo { ident, flatten, ty }| {
let s = ident.unraw().to_string();
if *flatten{
quote! { #ident : <#ty as sea_orm::FromQueryResult>::from_query_result(#arg_row, #arg_pre)? }
} else {
quote! { #ident : #arg_row.try_get(#arg_pre, #s)?}
tyt2y3 marked this conversation as resolved.
Show resolved Hide resolved
}
})
.collect();

Expand All @@ -38,9 +60,9 @@ pub fn expand_derive_from_query_result(
Ok(quote!(
#[automatically_derived]
impl #impl_generics sea_orm::FromQueryResult for #ident #ty_generics #where_clause {
fn from_query_result(row: &sea_orm::QueryResult, pre: &str) -> std::result::Result<Self, sea_orm::DbErr> {
fn from_query_result(#arg_row: &sea_orm::QueryResult, #arg_pre: &str) -> std::result::Result<Self, sea_orm::DbErr> {
Ok(Self {
#(#field: row.try_get(pre, #name)?),*
#(#field_query),*
})
}
}
Expand Down
26 changes: 21 additions & 5 deletions sea-orm-macros/src/derives/partial_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ use syn::Meta;

use self::util::GetAsKVMeta;

use super::util::field_attr_contain_key;

#[derive(Debug)]
enum Error {
InputNotStruct,
EntityNotSpecific,
NotSupportGeneric(Span),
BothFromColAndFromExpr(Span),
FlattenNotOnly(Span),
Syn(syn::Error),
}
#[derive(Debug, PartialEq, Eq)]
Expand All @@ -29,6 +32,8 @@ enum ColumnAs {
ColAlias { col: syn::Ident, field: String },
/// from a expr
Expr { expr: syn::Expr, field_name: String },
/// flatten
Flatten { ty: syn::Type },
}

struct DerivePartialModel {
Expand Down Expand Up @@ -75,6 +80,7 @@ impl DerivePartialModel {

let mut from_col = None;
let mut from_expr = None;
let flatten = field_attr_contain_key(&field, "flatten");

for attr in field.attrs.iter() {
if !attr.path.is_ident("sea_orm") {
Expand All @@ -97,8 +103,8 @@ impl DerivePartialModel {

let field_name = field.ident.unwrap();

let col_as = match (from_col, from_expr) {
(None, None) => {
let col_as = match (from_col, from_expr, flatten) {
(None, None, false) => {
if entity_ident.is_none() {
return Err(Error::EntityNotSpecific);
}
Expand All @@ -107,19 +113,23 @@ impl DerivePartialModel {
field_name.to_string().to_upper_camel_case()
))
}
(None, Some(expr)) => ColumnAs::Expr {
(None, Some(expr), false) => ColumnAs::Expr {
expr,
field_name: field_name.to_string(),
},
(Some(col), None) => {
(Some(col), None, false) => {
if entity_ident.is_none() {
return Err(Error::EntityNotSpecific);
}

let field = field_name.to_string();
ColumnAs::ColAlias { col, field }
}
(Some(_), Some(_)) => return Err(Error::BothFromColAndFromExpr(field_span)),
(None, None, true) => ColumnAs::Flatten { ty: field.ty },
(Some(_), _, true) | (_, Some(_), true) => {
return Err(Error::FlattenNotOnly(field_span))
}
(Some(_), Some(_), _) => return Err(Error::BothFromColAndFromExpr(field_span)),
};
column_as_list.push(col_as);
}
Expand Down Expand Up @@ -156,6 +166,9 @@ impl DerivePartialModel {
ColumnAs::Expr { expr, field_name } => {
quote!(let #select_ident = sea_orm::SelectColumns::select_column_as(#select_ident, #expr, #field_name);)
},
ColumnAs::Flatten { ty } => {
quote!(let #select_ident = <#ty as sea_orm::PartialModelTrait>::select_cols(#select_ident);)
}
});

quote! {
Expand Down Expand Up @@ -187,6 +200,9 @@ pub fn expand_derive_partial_model(input: syn::DeriveInput) -> syn::Result<Token
Err(Error::InputNotStruct) => Ok(quote_spanned! {
ident_span => compile_error!("you can only derive `DerivePartialModel` on named struct");
}),
Err(Error::FlattenNotOnly(span)) => Ok(quote_spanned! {
span => compile_error!("you can only derive `DerivePartialModel` on named struct");
}),
Err(Error::Syn(err)) => Err(err),
}
}
Expand Down
10 changes: 7 additions & 3 deletions sea-orm-macros/src/derives/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use quote::format_ident;
use syn::{punctuated::Punctuated, token::Comma, Field, Ident, Meta};

pub(crate) fn field_not_ignored(field: &Field) -> bool {
!field_attr_contain_key(field, "ignore")
}

pub(crate) fn field_attr_contain_key(field: &Field, key: &'static str) -> bool {
for attr in field.attrs.iter() {
if let Some(ident) = attr.path.get_ident() {
if ident != "sea_orm" {
Expand All @@ -16,15 +20,15 @@ pub(crate) fn field_not_ignored(field: &Field) -> bool {
for meta in list.iter() {
if let Meta::Path(path) = meta {
if let Some(name) = path.get_ident() {
if name == "ignore" {
return false;
if name == key {
return true;
}
}
}
}
}
}
true
false
}

pub(crate) fn format_field_ident(field: Field) -> Ident {
Expand Down
58 changes: 57 additions & 1 deletion sea-orm-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,26 @@ pub fn derive_active_enum(input: TokenStream) -> TokenStream {
/// num_of_fruits: i32,
/// }
/// ```
///
/// Can contain other `FromQueryResult` for a complex query result
/// ```
/// use sea_orm::{entity::prelude::*, FromQueryResult};
///
/// #[derive(Debug, FromQueryResult)]
/// struct Foo {
/// bar: i64,
/// }
///
/// #[derive(Debug, FromQueryResult)]
/// struct SelectResult {
/// name: String,
/// num_of_fruits: i32,
/// #[sea_orm(flatten)]
/// foo: Foo,
/// }
/// ```
#[cfg(feature = "derive")]
#[proc_macro_derive(FromQueryResult)]
#[proc_macro_derive(FromQueryResult, attributes(sea_orm))]
pub fn derive_from_query_result(input: TokenStream) -> TokenStream {
let DeriveInput {
ident,
Expand Down Expand Up @@ -781,6 +799,44 @@ pub fn derive_from_json_query_result(input: TokenStream) -> TokenStream {
/// sum: i32
/// }
/// ```
///
/// Can contain orther `PartialModel` for complex col constitution
/// ```
/// use sea_orm::{entity::prelude::*, sea_query::Expr, DerivePartialModel, FromQueryResult};
///
/// #[derive(Debug, FromQueryResult, DerivePartialModel)]
/// struct SelectResult {
/// #[sea_orm(from_expr = "Expr::val(1).add(1)")]
/// sum: i32,
/// #[sea_orm(flatten)]
/// foo: Foo,
/// }
///
/// #[derive(Debug, FromQueryResult, DerivePartialModel)]
/// struct Foo {
/// #[sea_orm(from_expr = "Expr::val(12).add(2)")]
/// bar: i64,
/// }
/// ```
/// Note: the `flatten` cannot use with `from_expr` or `from_col`,
/// or is cannot compile
/// ```compile_fail
/// use sea_orm::{entity::prelude::*, sea_query::Expr, DerivePartialModel, FromQueryResult};
///
/// #[derive(Debug, FromQueryResult, DerivePartialModel)]
/// struct SelectResult {
/// #[sea_orm(from_expr = "Expr::val(1).add(1)")]
/// sum: i32,
/// #[sea_orm(flatten, from_expr = "Expr::val(11).div(5)")]
/// foo: Foo
/// }
///
/// #[derive(Debug, FromQueryResult, DerivePartialModel)]
/// struct Foo{
/// #[sea_orm(from_expr = "Expr::val(12).add(2)")]
/// bar: i64
/// }
/// ```
#[cfg(feature = "derive")]
#[proc_macro_derive(DerivePartialModel, attributes(sea_orm))]
pub fn derive_partial_model(input: TokenStream) -> TokenStream {
Expand Down
18 changes: 18 additions & 0 deletions tests/partial_model_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,21 @@ struct FieldFromExpr {
#[sea_orm(from_expr = "Expr::col(Column::Id).equals(Column::Foo)")]
_bar: bool,
}

#[derive(FromQueryResult, DerivePartialModel)]
struct NestWithOther {
#[sea_orm(flatten)]
_flatten1: FieldFromDiffNameColumnTest,
#[sea_orm(flatten)]
_flatten2: FieldFromExpr,
}
#[derive(FromQueryResult, DerivePartialModel)]
#[sea_orm(entity = "Entity")]
struct MixedNestTest {
#[sea_orm(flatten)]
_flatten1: FieldFromDiffNameColumnTest,
#[sea_orm(from_col = "id")]
_id: i32,
#[sea_orm(from_expr = "Column::Bar2.sum()")]
_sum: f64,
}
Loading