Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
3 changes: 2 additions & 1 deletion sea-orm-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ pluralizer = { version = "0.5" }
proc-macro-crate = { version = "3.2.0", optional = true }
proc-macro2 = { version = "1", default-features = false }
quote = { version = "1", default-features = false }
syn = { version = "2", default-features = false, features = [
syn = { version = "3", default-features = false, features = [
"parsing",
"proc-macro",
"derive",
"printing",
"extra-traits",
"full",
] }
unicode-ident = { version = "1" }

Expand Down
18 changes: 9 additions & 9 deletions sea-orm-macros/src/derives/active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::{Data, DataStruct, Expr, Fields, LitStr, Type, Visibility};

pub(crate) struct DeriveActiveModel {
pub(crate) struct DeriveActiveModel<'a> {
model: Ident,
vis: Visibility,
vis: &'a Visibility,
fields: Vec<Ident>,
names: Vec<Ident>,
types: Vec<Type>,
types: Vec<&'a Type>,
}

impl DeriveActiveModel {
pub fn new(vis: &Visibility, ident: &Ident, data: &Data) -> syn::Result<Self> {
impl<'a> DeriveActiveModel<'a> {
pub fn new(vis: &'a Visibility, ident: &Ident, data: &'a Data) -> syn::Result<Self> {
let all_fields = match data {
Data::Struct(DataStruct {
fields: Fields::Named(named),
Expand Down Expand Up @@ -60,22 +60,22 @@ impl DeriveActiveModel {
})?;

names.push(ident);
types.push(field.ty.clone());
types.push(&field.ty);
}

Ok(DeriveActiveModel {
model: ident.clone(),
vis: vis.clone(),
vis,
fields,
names,
types,
})
}
}

impl DeriveActiveModel {
impl<'a> DeriveActiveModel<'a> {
fn define_active_model(&self) -> TokenStream {
let vis = &self.vis;
let vis = self.vis;
let fields = &self.fields;
let types = &self.types;
quote!(
Expand Down
17 changes: 9 additions & 8 deletions sea-orm-macros/src/derives/active_model_ex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use super::util::{
use heck::ToUpperCamelCase;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
use syn::{Attribute, Data, LitStr, PathArguments, Type, TypePath, Visibility};
use syn::{Attribute, Data, LitStr, PathArguments, Type, TypePath, Visibility, parse_quote};

enum RelationAttr {
BelongsTo {
Expand Down Expand Up @@ -79,8 +79,8 @@ impl RelationAttr {
));
}
Ok(Some(Self::BelongsTo {
from: RelationColumns::from_lit(from.clone())?,
relation_enum: Some(relation_enum.clone()),
from: RelationColumns::from_lit(from)?,
relation_enum: Some(parse_quote!(#relation_enum)),
}))
}
compound_attr::SeaOrm {
Expand All @@ -101,7 +101,7 @@ impl RelationAttr {
));
}
Ok(Some(Self::HasManySelf {
relation_enum: relation_enum.clone(),
relation_enum: parse_quote!(#relation_enum),
}))
}
compound_attr::SeaOrm {
Expand Down Expand Up @@ -130,10 +130,10 @@ impl RelationAttr {
));
}
Ok(Some(Self::BelongsTo {
from: RelationColumns::from_lit(attrs.from.clone().ok_or_else(|| {
from: RelationColumns::from_lit(attrs.from.as_ref().ok_or_else(|| {
syn::Error::new_spanned(field_ident, "belongs_to must specify `from`")
})?)?,
relation_enum: attrs.relation_enum.clone(),
relation_enum: attrs.relation_enum.as_ref().map(|lit| parse_quote!(#lit)),
}))
}
compound_attr::SeaOrm {
Expand Down Expand Up @@ -446,7 +446,8 @@ impl<'a> ActiveModelSetter<'a> {
fn expand_compound(&self, compound_type: &CompoundType) -> syn::Result<TokenStream> {
let field_ident = self.field.ident;
let entity_path = &compound_type.entity;
let mut active_model_type = entity_path.path.clone();
let entity_path_path = &entity_path.path;
let mut active_model_type: syn::Path = parse_quote!(#entity_path_path);
let Some(segment) = active_model_type.segments.last_mut() else {
return Err(syn::Error::new_spanned(entity_path, "expected entity path"));
};
Expand Down Expand Up @@ -600,7 +601,7 @@ impl ActiveModelActionTokens {
RelationAttr::HasManySelf { relation_enum } => {
Some(Relation::HasManySelf(HasManySelfField {
ident,
relation_variant: relation_enum.clone(),
relation_variant: parse_quote!(#relation_enum),
}))
}
RelationAttr::BelongsTo {
Expand Down
2 changes: 1 addition & 1 deletion sea-orm-macros/src/derives/arrow_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ pub fn expand_derive_arrow_schema(

fields_info.push(ArrowFieldInfo {
name: resolved_name,
field_type: field_type.clone(),
field_type: syn::parse_quote!(#field_type),
column_type_str,
nullable,
arrow_attrs,
Expand Down
57 changes: 50 additions & 7 deletions sea-orm-macros/src/derives/entity_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ struct EntityLoaderOutput {
}

impl EntityLoaderField {
fn entity_path(&self) -> syn::Path {
let path = &self.entity.path;
syn::parse_quote!(#path)
}

fn expand_loader_with_field_into(&self, output: &mut EntityLoaderOutput) {
let field = &self.field;

Expand All @@ -62,7 +67,7 @@ impl EntityLoaderField {
EntityLoaderFieldKind::HasOne
| EntityLoaderFieldKind::HasMany
| EntityLoaderFieldKind::ManyToMany => {
let mut entity_module = self.entity.path.clone();
let mut entity_module = self.entity_path();
entity_module.segments.pop();
entity_module
.segments
Expand All @@ -81,9 +86,10 @@ impl EntityLoaderField {
}

fn expand_relation_tuple_with_param_into(&self, output: &mut EntityLoaderOutput) {
let mut entity_module = self.entity.path.clone();
let mut entity_module = self.entity_path();
entity_module.segments.pop();
let mut related_entity = entity_module.clone();
// syn 3's `pop()` leaves a trailing `::`, which `push` reuses as the separator.
let mut related_entity: syn::Path = entity_module.clone();
related_entity.segments.push(syn::parse_quote!(Entity));
let mut related_relation = entity_module;
related_relation.segments.push(syn::parse_quote!(Relation));
Expand Down Expand Up @@ -294,7 +300,7 @@ impl EntityLoaderField {
} else {
quote!()
};
let mut entity_module = self.entity.path.clone();
let mut entity_module = self.entity_path();
entity_module.segments.pop();
entity_module.segments.push(syn::parse_quote!(EntityLoader));

Expand Down Expand Up @@ -345,7 +351,7 @@ impl EntityLoaderField {
} else {
quote!()
};
let mut entity_module = self.entity.path.clone();
let mut entity_module = self.entity_path();
entity_module.segments.pop();
entity_module.segments.push(syn::parse_quote!(EntityLoader));

Expand Down Expand Up @@ -403,7 +409,7 @@ impl EntityLoaderField {
} else {
quote!()
};
let mut entity_module = self.entity.path.clone();
let mut entity_module = self.entity_path();
entity_module.segments.pop();
entity_module.segments.push(syn::parse_quote!(EntityLoader));

Expand Down Expand Up @@ -454,7 +460,7 @@ impl EntityLoaderField {
} else {
quote!()
};
let mut entity_module = self.entity.path.clone();
let mut entity_module = self.entity_path();
entity_module.segments.pop();
entity_module.segments.push(syn::parse_quote!(EntityLoader));

Expand Down Expand Up @@ -994,3 +1000,40 @@ pub fn expand_entity_loader(vis: &Visibility, schema: EntityLoaderSchema) -> Tok

}
}

#[cfg(test)]
mod test {
use super::*;
use proc_macro2::Span;
use syn::parse_quote;

#[test]
fn expand_relation_tuple_multi_segment_entity_path() {
// Verifies the multi-segment entity module path is extended correctly
// (syn 3 `pop()` leaves a trailing `::`, reused as the separator).
let field = EntityLoaderField {
field: syn::Ident::new("cakes", Span::call_site()),
entity: parse_quote!(crate::entities::cake::Entity),
relation_enum: None,
kind: EntityLoaderFieldKind::HasMany,
};

let mut output = EntityLoaderOutput::default();
field.expand_relation_tuple_with_param_into(&mut output);

let generated = output.with_param_impls.to_string();
assert!(
generated.contains("crate :: entities :: cake :: Entity"),
"expected related entity path, got: {generated}"
);
assert!(
generated.contains("crate :: entities :: cake :: Relation"),
"expected related relation path, got: {generated}"
);
// No dangling or doubled `::` in the emitted paths.
assert!(
!generated.contains(":: ::"),
"unexpected double colon in generated path: {generated}"
);
}
}
2 changes: 1 addition & 1 deletion sea-orm-macros/src/derives/entity_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ pub fn expand_derive_entity_model(
ignore = true;
} else if meta.path.is_ident("primary_key") {
is_primary_key = true;
primary_key_types.push(field.ty.clone());
primary_key_types.push(&field.ty);
} else if meta.path.is_ident("nullable") {
nullable = true;
} else if meta.path.is_ident("indexed") {
Expand Down
9 changes: 7 additions & 2 deletions sea-orm-macros/src/derives/into_active_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,14 +140,16 @@ impl DeriveIntoActiveModel {
} = self;

let mut active_model_ident = active_model
.clone()
.as_ref()
.map(|am| syn::parse_quote!(#am))
.unwrap_or_else(|| syn::parse_str::<syn::Type>("ActiveModel").unwrap());

// Create a type alias for qualified types
let type_alias_definition = if is_qualified_type(&active_model_ident) {
let type_alias = format_ident!("ActiveModelFor{ident}");
let type_def = quote!( type #type_alias = #active_model_ident; );
active_model_ident = syn::Type::Path(syn::TypePath {
attrs: Vec::new(),
qself: None,
path: syn::Path {
leading_colon: None,
Expand Down Expand Up @@ -180,7 +182,10 @@ impl DeriveIntoActiveModel {
});

// Add custom field assignments from #[sea_orm(set(field = expr))]
let (set_idents, set_exprs): (Vec<_>, Vec<_>) = set_fields.iter().cloned().unzip();
let (set_idents, set_exprs): (Vec<_>, Vec<_>) = set_fields
.iter()
.map(|(id, expr)| (id.clone(), expr))
.unzip();
let expanded_sets = set_exprs.iter().map(|expr| {
quote!(
sea_orm::ActiveValue::Set(#expr)
Expand Down
14 changes: 7 additions & 7 deletions sea-orm-macros/src/derives/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ use quote::{format_ident, quote};
use std::iter::FromIterator;
use syn::{Attribute, Data, Expr, Ident, LitStr, Type};

pub(crate) struct DeriveModel {
pub(crate) struct DeriveModel<'a> {
column_idents: Vec<Ident>,
entity_ident: Ident,
field_idents: Vec<Ident>,
field_types: Vec<syn::Type>,
field_types: Vec<&'a syn::Type>,
ident: Ident,
ignore_attrs: Vec<bool>,
}

impl DeriveModel {
pub fn new(ident: &Ident, data: &Data, attrs: &[Attribute]) -> syn::Result<Self> {
impl<'a> DeriveModel<'a> {
pub fn new(ident: &Ident, data: &'a Data, attrs: &[Attribute]) -> syn::Result<Self> {
let fields = match data {
syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { named, .. }),
Expand All @@ -42,7 +42,7 @@ impl DeriveModel {
.map(|field| field.ident.as_ref().unwrap().clone())
.collect();

let field_types = fields.iter().map(|field| field.ty.clone()).collect();
let field_types = fields.iter().map(|field| &field.ty).collect();

let column_idents = fields
.iter()
Expand Down Expand Up @@ -188,11 +188,11 @@ impl DeriveModel {
)
}

pub fn impl_model_trait<'a>(&'a self) -> TokenStream {
pub fn impl_model_trait<'b>(&'b self) -> TokenStream {
let ident = &self.ident;
let entity_ident = &self.entity_ident;
let ignore_attrs = &self.ignore_attrs;
let ignore = |(ident, ignore): (&'a Ident, &bool)| -> Option<&'a Ident> {
let ignore = |(ident, ignore): (&'b Ident, &bool)| -> Option<&'b Ident> {
if *ignore { None } else { Some(ident) }
};
let field_idents: Vec<&Ident> = self
Expand Down
Loading