diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index 77e30196a..591da6f33 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -101,6 +101,10 @@ impl ActiveEnum { } else if meta.path.is_ident("num_value") { is_int = true; num_value = Some(meta.value()?.parse::()?); + } else if meta.path.is_ident("display_value") { + // This is a placeholder to prevent the `display_value` proc_macro attribute of `DeriveDisplay` + // to be considered unknown attribute parameter + meta.value()?.parse::()?; } else { return Err(meta.error(format!( "Unknown attribute parameter found: {:?}", @@ -379,14 +383,6 @@ impl ActiveEnum { } } - #[automatically_derived] - impl std::fmt::Display for #ident { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let v: sea_orm::sea_query::Value = ::to_value(&self).into(); - write!(f, "{}", v) - } - } - #impl_not_u8 ) } diff --git a/sea-orm-macros/src/derives/active_enum_display.rs b/sea-orm-macros/src/derives/active_enum_display.rs new file mode 100644 index 000000000..565e41933 --- /dev/null +++ b/sea-orm-macros/src/derives/active_enum_display.rs @@ -0,0 +1,104 @@ +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{LitInt, LitStr}; + +enum Error { + InputNotEnum, + Syn(syn::Error), +} + +struct Display { + ident: syn::Ident, + variants: Vec, +} + +struct DisplayVariant { + ident: syn::Ident, + display_value: TokenStream, +} + +impl Display { + fn new(input: syn::DeriveInput) -> Result { + let ident = input.ident; + + let variant_vec = match input.data { + syn::Data::Enum(syn::DataEnum { variants, .. }) => variants, + _ => return Err(Error::InputNotEnum), + }; + + let mut variants = Vec::new(); + for variant in variant_vec { + let mut display_value = variant.ident.to_string().to_token_stream(); + for attr in variant.attrs.iter() { + if !attr.path().is_ident("sea_orm") { + continue; + } + attr.parse_nested_meta(|meta| { + if meta.path.is_ident("string_value") { + meta.value()?.parse::()?; + } else if meta.path.is_ident("num_value") { + meta.value()?.parse::()?; + } else if meta.path.is_ident("display_value") { + display_value = meta.value()?.parse::()?.to_token_stream(); + } else { + return Err(meta.error(format!( + "Unknown attribute parameter found: {:?}", + meta.path.get_ident() + ))); + } + + Ok(()) + }) + .map_err(Error::Syn)?; + } + variants.push(DisplayVariant { + ident: variant.ident, + display_value, + }); + } + Ok(Display { ident, variants }) + } + + fn expand(&self) -> syn::Result { + let expanded_impl_active_enum_display = self.impl_active_enum_display(); + + Ok(expanded_impl_active_enum_display) + } + + fn impl_active_enum_display(&self) -> TokenStream { + let Self { ident, variants } = self; + + let variant_idents: Vec<_> = variants + .iter() + .map(|variant| variant.ident.clone()) + .collect(); + + let variant_display: Vec<_> = variants + .iter() + .map(|variant| variant.display_value.to_owned()) + .collect(); + + quote!( + #[automatically_derived] + impl std::fmt::Display for #ident { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + #( Self::#variant_idents => #variant_display, )* + }) + } + } + ) + } +} + +pub fn expand_derive_active_enum_display(input: syn::DeriveInput) -> syn::Result { + let ident_span = input.ident.span(); + + match Display::new(input) { + Ok(model) => model.expand(), + Err(Error::InputNotEnum) => Ok(quote_spanned! { + ident_span => compile_error!("you can only derive EnumDisplay on enums"); + }), + Err(Error::Syn(e)) => Err(e), + } +} diff --git a/sea-orm-macros/src/derives/mod.rs b/sea-orm-macros/src/derives/mod.rs index 6d71d1a51..168f94eeb 100644 --- a/sea-orm-macros/src/derives/mod.rs +++ b/sea-orm-macros/src/derives/mod.rs @@ -1,4 +1,5 @@ mod active_enum; +mod active_enum_display; mod active_model; mod active_model_behavior; mod attributes; @@ -19,6 +20,7 @@ mod util; mod value_type; pub use active_enum::*; +pub use active_enum_display::*; pub use active_model::*; pub use active_model_behavior::*; pub use column::*; diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 07a26f053..c9d84d4a3 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -842,3 +842,13 @@ pub fn derive_value_type(input: TokenStream) -> TokenStream { Err(e) => e.to_compile_error().into(), } } + +#[cfg(feature = "derive")] +#[proc_macro_derive(DeriveDisplay, attributes(sea_orm))] +pub fn derive_active_enum_display(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + match derives::expand_derive_active_enum_display(input) { + Ok(ts) => ts.into(), + Err(e) => e.to_compile_error().into(), + } +} diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 27bc894d0..99e6351da 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -17,7 +17,7 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Using the derive macro -/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum, DeriveDisplay)] /// #[sea_orm( /// rs_type = "String", /// db_type = "String(Some(1))", @@ -85,7 +85,7 @@ use sea_query::{DynIden, Expr, Nullable, SimpleExpr, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Define the `Category` active enum -/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum, DeriveDisplay)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum Category { /// #[sea_orm(string_value = "B")] @@ -216,7 +216,7 @@ mod tests { } } - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm( rs_type = "String", db_type = "String(Some(1))", @@ -268,15 +268,15 @@ mod tests { ); assert_eq!(Category::values(), DeriveCategory::values()); - assert_eq!(format!("{}", DeriveCategory::Big), "'B'"); - assert_eq!(format!("{}", DeriveCategory::Small), "'S'"); + assert_eq!(format!("{}", DeriveCategory::Big), "Big"); + assert_eq!(format!("{}", DeriveCategory::Small), "Small"); } #[test] fn active_enum_derive_signed_integers() { macro_rules! test_num_value_int { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = -10)] @@ -293,7 +293,7 @@ mod tests { macro_rules! test_fallback_int { ($ident: ident, $fallback_type: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] #[repr(i32)] pub enum $ident { @@ -325,9 +325,9 @@ mod tests { assert_eq!($ident::db_type(), ColumnType::$col_def.def()); - assert_eq!(format!("{}", $ident::Big), "1"); - assert_eq!(format!("{}", $ident::Small), "0"); - assert_eq!(format!("{}", $ident::Negative), "-10"); + assert_eq!(format!("{}", $ident::Big), "Big"); + assert_eq!(format!("{}", $ident::Small), "Small"); + assert_eq!(format!("{}", $ident::Negative), "Negative"); }; } @@ -346,7 +346,7 @@ mod tests { fn active_enum_derive_unsigned_integers() { macro_rules! test_num_value_uint { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] @@ -361,7 +361,7 @@ mod tests { macro_rules! test_fallback_uint { ($ident: ident, $fallback_type: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum)] + #[derive(Debug, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] #[repr($fallback_type)] pub enum $ident { @@ -390,8 +390,8 @@ mod tests { assert_eq!($ident::db_type(), ColumnType::$col_def.def()); - assert_eq!(format!("{}", $ident::Big), "1"); - assert_eq!(format!("{}", $ident::Small), "0"); + assert_eq!(format!("{}", $ident::Big), "Big"); + assert_eq!(format!("{}", $ident::Small), "Small"); }; } @@ -459,7 +459,7 @@ mod tests { assert_eq!(PopOSTypos::try_from_value(&val.to_owned()), Ok(variant)); } - #[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[derive(Clone, Debug, PartialEq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm( rs_type = "String", db_type = "String(None)", diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 394ab47d2..f335c9c82 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -11,8 +11,8 @@ pub use crate::{ #[cfg(feature = "macros")] pub use crate::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, - DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveModel, - DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, + DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, + DeriveModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, }; pub use async_trait; diff --git a/src/lib.rs b/src/lib.rs index 15e13e595..c01c2fe75 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -349,7 +349,7 @@ pub use schema::*; #[cfg(feature = "macros")] pub use sea_orm_macros::{ DeriveActiveEnum, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, - DeriveCustomColumn, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, + DeriveCustomColumn, DeriveDisplay, DeriveEntity, DeriveEntityModel, DeriveIntoActiveModel, DeriveMigrationName, DeriveModel, DerivePartialModel, DerivePrimaryKey, DeriveRelatedEntity, DeriveRelation, FromJsonQueryResult, FromQueryResult, }; diff --git a/tests/active_enum_tests.rs b/tests/active_enum_tests.rs index c6bf47d4a..14ebb436b 100644 --- a/tests/active_enum_tests.rs +++ b/tests/active_enum_tests.rs @@ -798,4 +798,12 @@ mod tests { ) ); } + + #[test] + fn display_test() { + assert_eq!(format!("{}", Tea::BreakfastTea), "BreakfastTea"); + assert_eq!(format!("{}", DisplayTea::BreakfastTea), "Breakfast"); + assert_eq!(format!("{}", Tea::EverydayTea), "EverydayTea"); + assert_eq!(format!("{}", DisplayTea::EverydayTea), "Everyday"); + } } diff --git a/tests/common/features/sea_orm_active_enums.rs b/tests/common/features/sea_orm_active_enums.rs index 94289dfce..e5fe2981a 100644 --- a/tests/common/features/sea_orm_active_enums.rs +++ b/tests/common/features/sea_orm_active_enums.rs @@ -18,7 +18,7 @@ pub enum Color { White, } -#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] pub enum Tea { #[sea_orm(string_value = "EverydayTea")] @@ -53,3 +53,12 @@ pub enum MediaType { #[sea_orm(string_value = "3D")] _3D, } + +#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, DeriveDisplay)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] +pub enum DisplayTea { + #[sea_orm(string_value = "EverydayTea", display_value = "Everyday")] + EverydayTea, + #[sea_orm(string_value = "BreakfastTea", display_value = "Breakfast")] + BreakfastTea, +}