Merge pull request #1755 from SeaQL/derive_iden

Remove sea-query's Iden macro; introduce DeriveIden
This commit is contained in:
Chris Tsang 2023-07-13 21:51:26 +08:00 committed by GitHub
commit 4953e37a34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 356 additions and 61 deletions

View File

@ -548,6 +548,34 @@ impl ColumnTrait for Column {
* Replace the use of `SeaRc<T>` where `T` isn't `dyn Iden` with `RcOrArc<T>` https://github.com/SeaQL/sea-orm/pull/1661 * Replace the use of `SeaRc<T>` where `T` isn't `dyn Iden` with `RcOrArc<T>` https://github.com/SeaQL/sea-orm/pull/1661
* Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743 * Enabled `hashable-value` feature in SeaQuery, thus `Value::Float(NaN) == Value::Float(NaN)` would be true https://github.com/SeaQL/sea-orm/pull/1728, https://github.com/SeaQL/sea-orm/pull/1743
* The `DeriveActiveEnum` derive macro no longer provide `std::fmt::Display` implementation for the enum. You need to derive an extra `DeriveDisplay` macro alongside with `DeriveActiveEnum` derive macro. https://github.com/SeaQL/sea-orm/pull/1726 * The `DeriveActiveEnum` derive macro no longer provide `std::fmt::Display` implementation for the enum. You need to derive an extra `DeriveDisplay` macro alongside with `DeriveActiveEnum` derive macro. https://github.com/SeaQL/sea-orm/pull/1726
* `sea-query/derive` is no longer enabled by `sea-orm`, as such, `Iden` no longer works as a derive macro (it's still a trait). Instead, we are shipping a new macro `DeriveIden`:
```rust
// then:
#[derive(Iden)]
#[iden = "category"]
pub struct CategoryEnum;
#[derive(Iden)]
pub enum Tea {
Table,
#[iden = "EverydayTea"]
EverydayTea,
}
// now:
#[derive(DeriveIden)]
#[sea_orm(iden = "category")]
pub struct CategoryEnum;
#[derive(DeriveIden)]
pub enum Tea {
Table,
#[sea_orm(iden = "EverydayTea")]
EverydayTea,
}
```
## 0.11.3 - 2023-04-24 ## 0.11.3 - 2023-04-24

View File

@ -34,7 +34,7 @@ tracing = { version = "0.1", default-features = false, features = ["attributes",
rust_decimal = { version = "1", default-features = false, optional = true } rust_decimal = { version = "1", default-features = false, optional = true }
bigdecimal = { version = "0.3", default-features = false, optional = true } bigdecimal = { version = "0.3", default-features = false, optional = true }
sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] } sea-orm-macros = { version = "0.12.0-rc.4", path = "sea-orm-macros", default-features = false, features = ["strum"] }
sea-query = { version = "0.29.0-rc.2", features = ["thread-safe", "hashable-value"] } sea-query = { version = "0.29.0", default-features = false, features = ["thread-safe", "hashable-value", "backend-mysql", "backend-postgres", "backend-sqlite"] }
sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true } sea-query-binder = { version = "0.4.0-rc.2", default-features = false, optional = true }
strum = { version = "0.25", default-features = false } strum = { version = "0.25", default-features = false }
serde = { version = "1.0", default-features = false } serde = { version = "1.0", default-features = false }
@ -74,7 +74,7 @@ default = [
"with-uuid", "with-uuid",
"with-time", "with-time",
] ]
macros = ["sea-orm-macros/derive", "sea-query/derive"] macros = ["sea-orm-macros/derive"]
mock = [] mock = []
with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"] with-json = ["serde_json", "sea-query/with-json", "chrono?/serde", "time?/serde", "uuid?/serde", "sea-query-binder?/with-json", "sqlx?/json"]
with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"] with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono", "sqlx?/chrono"]

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Notes { enum Notes {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

View File

@ -32,8 +32,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Posts { enum Posts {
Table, Table,
Id, Id,

13
issues/1473/Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[workspace]
# A separate workspace
[package]
name = "sea-orm-issues-1473"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies.sea-orm]
path = "../../"
default-features = false
features = ["macros", "runtime-tokio-native-tls"]

16
issues/1473/src/main.rs Normal file
View File

@ -0,0 +1,16 @@
use sea_orm::{DeriveIden, Iden};
#[derive(DeriveIden)]
enum Character {
Table,
Id,
}
#[derive(DeriveIden)]
struct Glyph;
fn main() {
assert_eq!(Character::Table.to_string(), "character");
assert_eq!(Character::Id.to_string(), "id");
assert_eq!(Glyph.to_string(), "glyph");
}

View File

@ -38,8 +38,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
enum Post { enum Post {
Table, Table,
Id, Id,

View File

@ -0,0 +1,151 @@
use heck::ToSnakeCase;
use proc_macro2::{self, TokenStream};
use quote::{quote, quote_spanned};
use syn::{
punctuated::Punctuated, DataEnum, DataStruct, DeriveInput, Expr, Fields, LitStr, Variant,
};
fn must_be_valid_iden(name: &str) -> bool {
// can only begin with [a-z_]
name.chars()
.take(1)
.all(|c| c == '_' || c.is_ascii_alphabetic())
&& name.chars().all(|c| c == '_' || c.is_ascii_alphanumeric())
}
fn impl_iden_for_unit_struct(
ident: &proc_macro2::Ident,
new_iden: &str,
) -> proc_macro2::TokenStream {
let prepare = if must_be_valid_iden(new_iden) {
quote! {
fn prepare(&self, s: &mut dyn ::std::fmt::Write, q: sea_orm::sea_query::Quote) {
write!(s, "{}", q.left()).unwrap();
self.unquoted(s);
write!(s, "{}", q.right()).unwrap();
}
}
} else {
quote! {}
};
quote! {
impl sea_orm::sea_query::Iden for #ident {
#prepare
fn unquoted(&self, s: &mut dyn ::std::fmt::Write) {
write!(s, #new_iden).unwrap();
}
}
}
}
fn impl_iden_for_enum(
ident: &proc_macro2::Ident,
variants: Punctuated<Variant, syn::token::Comma>,
) -> proc_macro2::TokenStream {
let variants = variants.iter();
let mut all_valid = true;
let match_pair: Vec<TokenStream> = variants
.map(|v| {
let var_ident = &v.ident;
let var_name = if var_ident == "Table" {
ident
} else {
var_ident
};
let mut var_name = var_name.to_string().to_snake_case();
v.attrs
.iter()
.filter(|attr| attr.path().is_ident("sea_orm"))
.try_for_each(|attr| {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("iden") {
let litstr: LitStr = meta.value()?.parse()?;
var_name = litstr.value();
all_valid &= must_be_valid_iden(var_name.as_str());
} else {
// Reads the value expression to advance the parse stream.
// Some parameters do not have any value,
// so ignoring an error occurred here.
let _: Option<Expr> = meta.value().and_then(|v| v.parse()).ok();
}
Ok(())
})
})
.expect("something something");
quote! { Self::#var_ident => write!(s, "{}", #var_name).unwrap() }
})
.collect();
let match_arms: TokenStream = quote! { #(#match_pair),* };
let prepare = if all_valid {
quote! {
fn prepare(&self, s: &mut dyn ::std::fmt::Write, q: sea_orm::sea_query::Quote) {
write!(s, "{}", q.left()).unwrap();
self.unquoted(s);
write!(s, "{}", q.right()).unwrap();
}
}
} else {
quote! {}
};
quote! {
impl sea_orm::sea_query::Iden for #ident {
#prepare
fn unquoted(&self, s: &mut dyn ::std::fmt::Write) {
match self {
#match_arms
};
}
}
}
}
pub fn expand_derive_iden(input: DeriveInput) -> syn::Result<TokenStream> {
let DeriveInput { ident, data, .. } = input;
let mut new_iden: TokenStream = ident.to_string().to_snake_case().parse().unwrap();
input
.attrs
.iter()
.filter(|attr| attr.path().is_ident("sea_orm"))
.try_for_each(|attr| {
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("iden") {
let litstr: LitStr = meta.value()?.parse()?;
new_iden = syn::parse_str::<TokenStream>(&litstr.value())?;
} else {
// Reads the value expression to advance the parse stream.
// Some parameters do not have any value,
// so ignoring an error occurred here.
let _: Option<Expr> = meta.value().and_then(|v| v.parse()).ok();
}
Ok(())
})
})?;
// Currently we only support enums and unit structs
match data {
syn::Data::Enum(DataEnum { variants, .. }) => {
if variants.is_empty() {
Ok(TokenStream::new())
} else {
Ok(impl_iden_for_enum(&ident, variants))
}
}
syn::Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => Ok(impl_iden_for_unit_struct(
&ident,
new_iden.to_string().as_str(),
)),
_ => Ok(quote_spanned! {
ident.span() => compile_error!("you can only derive DeriveIden on unit struct or enum");
}),
}
}

View File

@ -4,6 +4,7 @@ mod active_model;
mod active_model_behavior; mod active_model_behavior;
mod attributes; mod attributes;
mod column; mod column;
mod derive_iden;
mod entity; mod entity;
mod entity_model; mod entity_model;
mod from_query_result; mod from_query_result;
@ -24,6 +25,7 @@ pub use active_enum_display::*;
pub use active_model::*; pub use active_model::*;
pub use active_model_behavior::*; pub use active_model_behavior::*;
pub use column::*; pub use column::*;
pub use derive_iden::*;
pub use entity::*; pub use entity::*;
pub use entity_model::*; pub use entity_model::*;
pub use from_query_result::*; pub use from_query_result::*;

View File

@ -852,3 +852,45 @@ pub fn derive_active_enum_display(input: TokenStream) -> TokenStream {
Err(e) => e.to_compile_error().into(), Err(e) => e.to_compile_error().into(),
} }
} }
/// The DeriveIden derive macro will implement `sea_orm::sea_query::Iden` for simplify Iden implementation.
///
/// ## Usage
///
/// ```rust
/// use sea_orm::DeriveIden;
///
/// #[derive(DeriveIden)]
/// pub enum Class {
/// Id,
/// Title,
/// Text,
/// }
///
/// #[derive(DeriveIden)]
/// struct Glyph;
/// ```
///
/// You can use iden = "" to customize the name
/// ```
/// use sea_orm::DeriveIden;
///
/// #[derive(DeriveIden)]
/// pub enum Class {
/// Id,
/// #[sea_orm(iden = "turtle")]
/// Title,
/// #[sea_orm(iden = "TeXt")]
/// Text,
/// }
/// ```
#[cfg(feature = "derive")]
#[proc_macro_derive(DeriveIden, attributes(sea_orm))]
pub fn derive_iden(input: TokenStream) -> TokenStream {
let derive_input = parse_macro_input!(input as DeriveInput);
match derives::expand_derive_iden(derive_input) {
Ok(token_stream) => token_stream.into(),
Err(e) => e.to_compile_error().into(),
}
}

View File

@ -6,12 +6,13 @@ use std::time::SystemTime;
use tracing::info; use tracing::info;
use sea_orm::sea_query::{ use sea_orm::sea_query::{
self, extension::postgres::Type, Alias, Expr, ForeignKey, Iden, IntoIden, JoinType, Order, self, extension::postgres::Type, Alias, Expr, ForeignKey, IntoIden, JoinType, Order, Query,
Query, SelectStatement, SimpleExpr, Table, SelectStatement, SimpleExpr, Table,
}; };
use sea_orm::{ use sea_orm::{
ActiveModelTrait, ActiveValue, Condition, ConnectionTrait, DbBackend, DbErr, DynIden, ActiveModelTrait, ActiveValue, Condition, ConnectionTrait, DbBackend, DbErr, DeriveIden,
EntityTrait, FromQueryResult, Iterable, QueryFilter, Schema, Statement, TransactionTrait, DynIden, EntityTrait, FromQueryResult, Iterable, QueryFilter, Schema, Statement,
TransactionTrait,
}; };
use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite}; use sea_schema::{mysql::MySql, postgres::Postgres, probe::SchemaProbe, sqlite::Sqlite};
@ -457,13 +458,13 @@ where
} }
} }
#[derive(Iden)] #[derive(DeriveIden)]
enum InformationSchema { enum InformationSchema {
#[iden = "information_schema"] #[sea_orm(iden = "information_schema")]
Schema, Schema,
#[iden = "TABLE_NAME"] #[sea_orm(iden = "TABLE_NAME")]
TableName, TableName,
#[iden = "CONSTRAINT_NAME"] #[sea_orm(iden = "CONSTRAINT_NAME")]
ConstraintName, ConstraintName,
TableConstraints, TableConstraints,
TableSchema, TableSchema,
@ -500,7 +501,7 @@ where
stmt stmt
} }
#[derive(Iden)] #[derive(DeriveIden)]
enum PgType { enum PgType {
Table, Table,
Typname, Typname,
@ -508,7 +509,7 @@ enum PgType {
Typelem, Typelem,
} }
#[derive(Iden)] #[derive(DeriveIden)]
enum PgNamespace { enum PgNamespace {
Table, Table,
Oid, Oid,

View File

@ -9,5 +9,5 @@ pub use async_trait;
pub use sea_orm::{ pub use sea_orm::{
self, self,
sea_query::{self, *}, sea_query::{self, *},
ConnectionTrait, DbErr, DeriveMigrationName, ConnectionTrait, DbErr, DeriveIden, DeriveMigrationName,
}; };

View File

@ -38,8 +38,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
pub enum Cake { pub enum Cake {
Table, Table,
Id, Id,

View File

@ -49,8 +49,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
pub enum Fruit { pub enum Fruit {
Table, Table,
Id, Id,

View File

@ -42,12 +42,11 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
pub enum Tea { pub enum Tea {
Table, Table,
#[iden = "EverydayTea"] #[sea_orm(iden = "EverydayTea")]
EverydayTea, EverydayTea,
#[iden = "BreakfastTea"] #[sea_orm(iden = "BreakfastTea")]
BreakfastTea, BreakfastTea,
} }

View File

@ -29,8 +29,7 @@ impl MigrationTrait for Migration {
} }
} }
/// Learn more at https://docs.rs/sea-query#iden #[derive(DeriveIden)]
#[derive(Iden)]
pub enum Cake { pub enum Cake {
Table, Table,
Name, Name,

View File

@ -37,7 +37,7 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType};
/// Small, /// Small,
/// } /// }
/// ///
/// #[derive(Debug, Iden)] /// #[derive(Debug, DeriveIden)]
/// pub struct CategoryEnum; /// pub struct CategoryEnum;
/// ///
/// impl ActiveEnum for Category { /// impl ActiveEnum for Category {
@ -182,8 +182,8 @@ mod tests {
Small, Small,
} }
#[derive(Debug, Iden)] #[derive(Debug, DeriveIden)]
#[iden = "category"] #[sea_orm(iden = "category")]
pub struct CategoryEnum; pub struct CategoryEnum;
impl ActiveEnum for Category { impl ActiveEnum for Category {

View File

@ -1,6 +1,6 @@
use crate::{EntityName, IdenStatic, IntoSimpleExpr, Iterable}; use crate::{EntityName, Iden, IdenStatic, IntoSimpleExpr, Iterable};
use sea_query::{ use sea_query::{
Alias, BinOper, DynIden, Expr, Iden, IntoIden, SeaRc, SelectStatement, SimpleExpr, Value, Alias, BinOper, DynIden, Expr, IntoIden, SeaRc, SelectStatement, SimpleExpr, Value,
}; };
use std::str::FromStr; use std::str::FromStr;
@ -381,13 +381,21 @@ impl ColumnDef {
} }
} }
#[derive(Iden)]
struct Text; struct Text;
#[derive(Iden)]
#[iden = "text[]"]
struct TextArray; struct TextArray;
impl Iden for Text {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "text").unwrap();
}
}
impl Iden for TextArray {
fn unquoted(&self, s: &mut dyn std::fmt::Write) {
write!(s, "text[]").unwrap();
}
}
fn cast_enum_as<C, F>(expr: Expr, col: &C, f: F) -> SimpleExpr fn cast_enum_as<C, F>(expr: Expr, col: &C, f: F) -> SimpleExpr
where where
C: ColumnTrait, C: ColumnTrait,

View File

@ -11,8 +11,9 @@ pub use crate::{
#[cfg(feature = "macros")] #[cfg(feature = "macros")]
pub use crate::{ pub use crate::{
DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn,
DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIden,
DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, DeriveIntoActiveModel, DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation,
FromJsonQueryResult,
}; };
pub use async_trait; pub use async_trait;

View File

@ -349,15 +349,13 @@ pub use schema::*;
#[cfg(feature = "macros")] #[cfg(feature = "macros")]
pub use sea_orm_macros::{ pub use sea_orm_macros::{
DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn,
DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIden,
DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveIntoActiveModel, DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey,
DeriveRelation, FromJsonQueryResult, FromQueryResult, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, FromQueryResult,
}; };
pub use sea_query; pub use sea_query;
pub use sea_query::Iden; pub use sea_query::Iden;
#[cfg(feature = "macros")]
pub use sea_query::Iden as DeriveIden;
pub use sea_orm_macros::EnumIter; pub use sea_orm_macros::EnumIter;
pub use strum; pub use strum;

View File

@ -0,0 +1,50 @@
pub mod common;
pub use common::{features::*, setup::*, TestContext};
use sea_orm::entity::prelude::*;
use sea_orm_macros::DeriveIden;
#[derive(DeriveIden)]
pub enum ClassName {
Table,
Id,
Title,
Text,
}
#[derive(DeriveIden)]
pub enum Book {
Table,
Id,
#[sea_orm(iden = "turtle")]
Title,
#[sea_orm(iden = "TeXt")]
Text,
#[sea_orm(iden = "ty_pe")]
Type,
}
#[derive(DeriveIden)]
struct GlyphToken;
#[derive(DeriveIden)]
#[sea_orm(iden = "weRd")]
struct Word;
#[test]
fn main() -> Result<(), DbErr> {
assert_eq!(ClassName::Table.to_string(), "class_name");
assert_eq!(ClassName::Id.to_string(), "id");
assert_eq!(ClassName::Title.to_string(), "title");
assert_eq!(ClassName::Text.to_string(), "text");
assert_eq!(Book::Id.to_string(), "id");
assert_eq!(Book::Table.to_string(), "book");
assert_eq!(Book::Title.to_string(), "turtle");
assert_eq!(Book::Text.to_string(), "TeXt");
assert_eq!(Book::Type.to_string(), "ty_pe");
assert_eq!(GlyphToken.to_string(), "glyph_token");
assert_eq!(Word.to_string(), "weRd");
Ok(())
}