Skip to content
Merged
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
15 changes: 6 additions & 9 deletions sea-orm-macros/src/derives/entity_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,8 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
for field in fields.named {
if let Some(ident) = &field.ident {
let original_field_name = trim_starting_raw_identifier(ident);
let mut field_name = Ident::new(
&original_field_name.to_upper_camel_case(),
Span::call_site(),
);
let mut field_name =
Ident::new(&original_field_name.to_upper_camel_case(), ident.span());

let mut nullable = false;
let mut default_value = None;
Expand Down Expand Up @@ -275,7 +273,7 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res
field_name = enum_name;
}

field_name = Ident::new(&escape_rust_keyword(field_name), Span::call_site());
field_name = Ident::new(&escape_rust_keyword(field_name), ident.span());

let variant_attrs = match &column_name {
Some(column_name) => quote! {
Expand Down Expand Up @@ -321,15 +319,14 @@ pub fn expand_derive_entity_model(data: Data, attrs: Vec<Attribute>) -> syn::Res

let field_type = if field_type.starts_with("Option<") {
nullable = true;
&field_type[7..(field_type.len() - 1)] // Extract `T` out of `Option<T>`
&field_type["Option<".len()..(field_type.len() - 1)] // Extract `T` out of `Option<T>`
} else {
field_type.as_str()
};
let field_span = field.span();

let sea_query_col_type = crate::derives::sql_type_match::column_type_match(
sql_type, field_type, field_span,
);
let sea_query_col_type =
super::value_type_match::column_type_expr(sql_type, field_type, field_span);

let col_def =
quote! { sea_orm::prelude::ColumnTypeTrait::def(#sea_query_col_type) };
Expand Down
4 changes: 3 additions & 1 deletion sea-orm-macros/src/derives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ mod partial_model;
mod primary_key;
mod related_entity;
mod relation;
mod sql_type_match;
mod try_getable_from_json;
mod typed_column;
mod util;
mod value_type;
mod value_type_match;

pub use active_enum::*;
pub use active_enum_display::*;
Expand All @@ -41,4 +42,5 @@ pub use primary_key::*;
pub use related_entity::*;
pub use relation::*;
pub use try_getable_from_json::*;
pub use typed_column::*;
pub use value_type::*;
8 changes: 7 additions & 1 deletion sea-orm-macros/src/derives/model_ex.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::attributes::compound_attr;
use super::entity_loader::{EntityLoaderField, EntityLoaderSchema, expand_entity_loader};
use super::model::DeriveModel;
use super::util::{format_field_ident_ref, is_compound_field};
use super::{expand_typed_column, model::DeriveModel};
use heck::ToUpperCamelCase;
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote};
Expand Down Expand Up @@ -295,11 +295,15 @@ pub fn expand_derive_model_ex(
quote!()
};

let (typed_column, typed_column_const) = expand_typed_column(&data)?;

let (entity_find_by_key, loader_filter_by_key) = expand_find_by_unique_key(unique_keys);

let entity_loader = expand_entity_loader(entity_loader_schema);

Ok(quote! {
#typed_column

#impl_from_model

#impl_model_trait
Expand All @@ -313,6 +317,8 @@ pub fn expand_derive_model_ex(
#entity_loader

impl Entity {
#typed_column_const

#entity_find_by_key
}

Expand Down
46 changes: 0 additions & 46 deletions sea-orm-macros/src/derives/sql_type_match.rs

This file was deleted.

118 changes: 118 additions & 0 deletions sea-orm-macros/src/derives/typed_column.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use super::util::{
escape_rust_keyword, format_field_ident_ref, is_compound_field, trim_starting_raw_identifier,
};
use heck::ToUpperCamelCase;
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{Data, Expr, Fields, Lit, spanned::Spanned};

/// First is `struct TypedColumn`, second is the `const COLUMN`
pub fn expand_typed_column(data: &Data) -> syn::Result<(TokenStream, TokenStream)> {
let mut column_fields = Vec::new();
let mut column_types = Vec::new();
let mut column_values = Vec::new();

if let Data::Struct(item_struct) = &data {
if let Fields::Named(fields) = &item_struct.fields {
for field in &fields.named {
if let Some(ident) = &field.ident {
let field_name = trim_starting_raw_identifier(ident);
let mut field_name =
Ident::new(&field_name.to_upper_camel_case(), ident.span());

let field_type = &field.ty;
let field_type = quote! { #field_type }
.to_string() // e.g.: "Option < String >"
.replace(' ', ""); // Remove spaces

let mut ignore = false;
let mut column_type = None;

for attr in field.attrs.iter() {
if attr.path().is_ident("sea_orm") {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("column_type") {
let lit = meta.value()?.parse()?;
if let Lit::Str(litstr) = lit {
column_type = Some(litstr.value());
} else {
return Err(
meta.error(format!("Invalid column_type {lit:?}"))
);
}
} else if meta.path.is_ident("enum_name") {
let lit = meta.value()?.parse()?;
if let Lit::Str(litstr) = lit {
let ty: Ident = syn::parse_str(&litstr.value())?;
field_name = ty;
} else {
return Err(
meta.error(format!("Invalid enum_name {lit:?}"))
);
}
} else if meta.path.is_ident("ignore") {
ignore = true;
} else {
// Reads the value expression to advance the parse stream.
let _: Option<Expr> = meta.value().and_then(|v| v.parse()).ok();
}

Ok(())
})?;
}
}

if ignore {
continue;
}

if is_compound_field(&field_type) {
continue;
}

field_name = Ident::new(&escape_rust_keyword(field_name), ident.span());

let field_type = &field.ty;
let field_type = quote! { #field_type }
.to_string() // e.g.: "Option < String >"
.replace(' ', ""); // Remove spaces

column_fields.push(format_field_ident_ref(field));
let wrapper = super::value_type_match::column_type_wrapper(
&column_type,
&field_type,
field.span(),
);
column_types.push(if let Some(wrapper) = &wrapper {
quote!(sea_orm::#wrapper<Entity>)
} else {
quote!(Column)
});
column_values.push(if let Some(wrapper) = &wrapper {
quote!(sea_orm::#wrapper(Column::#field_name))
} else {
quote!(Column::#field_name)
});
}
}
}
}

Ok((
quote! {
#[doc = " Generated by sea-orm-macros"]
pub struct TypedColumn {
#(
#[doc = " Generated by sea-orm-macros"]
pub #column_fields: #column_types
),*
}
},
quote! {
#[doc = " Generated by sea-orm-macros"]
pub const COLUMN: TypedColumn = TypedColumn {
#(#column_fields: #column_values),*
};
},
))
}
5 changes: 2 additions & 3 deletions sea-orm-macros/src/derives/util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use heck::ToUpperCamelCase;
use quote::format_ident;
use syn::{Field, Ident, Meta, MetaNameValue, punctuated::Punctuated, token::Comma};

pub(crate) fn field_not_ignored(field: &Field) -> bool {
Expand Down Expand Up @@ -45,11 +44,11 @@ pub(crate) fn is_compound_field(field_type: &str) -> bool {
}

pub(crate) fn format_field_ident(field: Field) -> Ident {
format_field_ident_ref(&field)
field.ident.unwrap()
}

pub(crate) fn format_field_ident_ref(field: &Field) -> Ident {
format_ident!("{}", field.ident.as_ref().unwrap().to_string())
field.ident.clone().unwrap()
}

pub(crate) fn trim_starting_raw_identifier<T>(string: T) -> String
Expand Down
6 changes: 3 additions & 3 deletions sea-orm-macros/src/derives/value_type.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::sql_type_match::{array_type_match, can_try_from_u64, column_type_match};
use super::value_type_match::{array_type_expr, can_try_from_u64, column_type_expr};
use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{DataEnum, Lit, Type, spanned::Spanned};
Expand Down Expand Up @@ -116,8 +116,8 @@ impl DeriveValueTypeStruct {
};
let field_span = field.span();

let column_type = column_type_match(col_type, field_type, field_span);
let array_type = array_type_match(arr_type, field_type, field_span);
let column_type = column_type_expr(col_type, field_type, field_span);
let array_type = array_type_expr(arr_type, field_type, field_span);
let can_try_from_u64 = can_try_from_u64(field_type);

Ok(Self {
Expand Down
102 changes: 102 additions & 0 deletions sea-orm-macros/src/derives/value_type_match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use proc_macro2::{Span, TokenStream};
use quote::quote_spanned;
use syn::{Ident, LitStr, Type};

pub fn column_type_expr(
column_type: Option<TokenStream>,
field_type: &str,
field_span: Span,
) -> TokenStream {
match column_type {
Some(column_type) => {
quote_spanned! { field_span => sea_orm::prelude::ColumnType::#column_type }
}
None => {
let ty: Type = LitStr::new(field_type, field_span)
.parse()
.expect("field type error");
quote_spanned! { field_span => <#ty as sea_orm::sea_query::ValueType>::column_type() }
}
}
}

pub fn column_type_wrapper(
column_type: &Option<String>,
field_type: &str,
field_span: Span,
) -> Option<Ident> {
match column_type {
Some(_) => None, // TODO may be we can deduce column type
None => match field_type {
"String" => Some("StringColumn"),
"Vec<u8>" => Some("BytesColumn"),
"Uuid" => Some("UuidColumn"),
"IpNetwork" => Some("IpNetworkColumn"),
"Json" | "serde_json::Value" => Some("JsonColumn"),
"Timestamp" => Some("DateTimeLikeColumn"),
_ => {
if is_numeric_column(field_type) {
Some("NumericColumn")
} else if field_type.starts_with("Vec<") {
let field_type = &field_type["Vec<".len()..field_type.len() - 1];
if is_numeric_column(field_type) {
Some("NumericArrayColumn")
} else {
Some("GenericArrayColumn")
}
} else if field_type.contains("DateTime") {
Some("DateTimeLikeColumn")
} else if field_type.contains("Date") {
Some("DateLikeColumn")
} else if field_type.contains("Time") {
Some("TimeLikeColumn")
} else {
None
}
}
}
.map(|ty| Ident::new(ty, field_span)),
}
}

fn is_numeric_column(ty: &str) -> bool {
matches!(
ty,
"i8" | "i16"
| "i32"
| "i64"
| "u8"
| "u16"
| "u32"
| "u64"
| "f32"
| "f64"
| "Decimal"
| "BigDecimal"
)
}

pub fn array_type_expr(
array_type: Option<TokenStream>,
field_type: &str,
field_span: Span,
) -> TokenStream {
match array_type {
Some(array_type) => {
quote_spanned! { field_span => sea_orm::sea_query::ArrayType::#array_type }
}
None => {
let ty: Type = LitStr::new(field_type, field_span)
.parse()
.expect("field type error");
quote_spanned! { field_span => <#ty as sea_orm::sea_query::ValueType>::array_type() }
}
}
}

pub fn can_try_from_u64(field_type: &str) -> bool {
matches!(
field_type,
"i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" | "u64"
)
}
Loading
Loading