diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index cfd5454d..acb60a36 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -1,3 +1,4 @@ +use heck::CamelCase; use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; use syn::{punctuated::Punctuated, token::Comma, Lit, LitInt, LitStr, Meta}; @@ -10,6 +11,7 @@ enum Error { struct ActiveEnum { ident: syn::Ident, + enum_name: String, rs_type: TokenStream, db_type: TokenStream, is_string: bool, @@ -27,6 +29,7 @@ impl ActiveEnum { let ident_span = input.ident.span(); let ident = input.ident; + let mut enum_name = ident.to_string().to_camel_case(); let mut rs_type = Err(Error::TT(quote_spanned! { ident_span => compile_error!("Missing macro attribute `rs_type`"); })); @@ -52,8 +55,22 @@ impl ActiveEnum { } } else if name == "db_type" { if let Lit::Str(litstr) = &nv.lit { - db_type = syn::parse_str::(&litstr.value()) - .map_err(Error::Syn); + let s = litstr.value(); + match s.as_ref() { + "Enum" => { + db_type = Ok(quote! { + Enum(Self::name(), Self::values()) + }) + } + _ => { + db_type = syn::parse_str::(&s) + .map_err(Error::Syn); + } + } + } + } else if name == "enum_name" { + if let Lit::Str(litstr) = &nv.lit { + enum_name = litstr.value(); } } } @@ -125,6 +142,7 @@ impl ActiveEnum { Ok(ActiveEnum { ident, + enum_name, rs_type: rs_type?, db_type: db_type?, is_string, @@ -141,6 +159,7 @@ impl ActiveEnum { fn impl_active_enum(&self) -> TokenStream { let Self { ident, + enum_name, rs_type, db_type, is_string, @@ -181,6 +200,10 @@ impl ActiveEnum { impl sea_orm::ActiveEnum for #ident { type Value = #rs_type; + fn name() -> String { + #enum_name.to_owned() + } + fn to_value(&self) -> Self::Value { match self { #( Self::#variant_idents => #variant_values, )* diff --git a/sea-orm-macros/src/lib.rs b/sea-orm-macros/src/lib.rs index 1db680b0..00540aa4 100644 --- a/sea-orm-macros/src/lib.rs +++ b/sea-orm-macros/src/lib.rs @@ -509,6 +509,9 @@ pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream { /// - `db_type`: Define `ColumnType` returned by `ActiveEnum::db_type()` /// - Possible values: all available enum variants of `ColumnType`, e.g. `String(None)`, `String(Some(1))`, `Integer` /// - Note that value has to be passed as string, i.e. `db_type = "Integer"` +/// - `enum_name`: Define `String` returned by `ActiveEnum::name()` +/// - This attribute is optional with default value being the name of enum in camel-case +/// - Note that value has to be passed as string, i.e. `db_type = "Integer"` /// /// - For enum variant /// - `string_value` or `num_value`: diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 5eb77b9f..104b573f 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -1,4 +1,4 @@ -use crate::{ColumnDef, DbErr, TryGetable}; +use crate::{ColumnDef, DbErr, Iterable, TryGetable}; use sea_query::{Nullable, Value, ValueType}; /// A Rust representation of enum defined in database. @@ -17,8 +17,12 @@ use sea_query::{Nullable, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Using the derive macro -/// #[derive(Debug, PartialEq, DeriveActiveEnum)] -/// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] +/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] +/// #[sea_orm( +/// rs_type = "String", +/// db_type = "String(Some(1))", +/// enum_name = "category" +/// )] /// pub enum DeriveCategory { /// #[sea_orm(string_value = "B")] /// Big, @@ -27,7 +31,7 @@ use sea_query::{Nullable, Value, ValueType}; /// } /// /// // Implementing it manually -/// #[derive(Debug, PartialEq)] +/// #[derive(Debug, PartialEq, EnumIter)] /// pub enum Category { /// Big, /// Small, @@ -38,6 +42,11 @@ use sea_query::{Nullable, Value, ValueType}; /// type Value = String; /// /// // Will be atomically generated by `DeriveActiveEnum` +/// fn name() -> String { +/// "category".to_owned() +/// } +/// +/// // Will be atomically generated by `DeriveActiveEnum` /// fn to_value(&self) -> Self::Value { /// match self { /// Self::Big => "B", @@ -71,7 +80,7 @@ use sea_query::{Nullable, Value, ValueType}; /// use sea_orm::entity::prelude::*; /// /// // Define the `Category` active enum -/// #[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] +/// #[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] /// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] /// pub enum Category { /// #[sea_orm(string_value = "B")] @@ -95,10 +104,13 @@ use sea_query::{Nullable, Value, ValueType}; /// /// impl ActiveModelBehavior for ActiveModel {} /// ``` -pub trait ActiveEnum: Sized { +pub trait ActiveEnum: Sized + Iterable { /// Define the Rust type that each enum variant represents. type Value: Into + ValueType + Nullable + TryGetable; + /// Get the name of enum + fn name() -> String; + /// Convert enum variant into the corresponding value. fn to_value(&self) -> Self::Value; @@ -107,6 +119,11 @@ pub trait ActiveEnum: Sized { /// Get the database column definition of this active enum. fn db_type() -> ColumnDef; + + /// Get the name of all enum variants + fn values() -> Vec { + Self::iter().map(|s| s.to_value()).collect() + } } #[cfg(test)] @@ -117,7 +134,7 @@ mod tests { #[test] fn active_enum_string() { - #[derive(Debug, PartialEq)] + #[derive(Debug, PartialEq, EnumIter)] pub enum Category { Big, Small, @@ -126,6 +143,10 @@ mod tests { impl ActiveEnum for Category { type Value = String; + fn name() -> String { + "category".to_owned() + } + fn to_value(&self) -> Self::Value { match self { Self::Big => "B", @@ -150,8 +171,12 @@ mod tests { } } - #[derive(Debug, PartialEq, DeriveActiveEnum)] - #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[sea_orm( + rs_type = "String", + db_type = "String(Some(1))", + enum_name = "category" + )] pub enum DeriveCategory { #[sea_orm(string_value = "B")] Big, @@ -195,13 +220,16 @@ mod tests { assert_eq!(Category::db_type(), ColumnType::String(Some(1)).def()); assert_eq!(DeriveCategory::db_type(), ColumnType::String(Some(1)).def()); + + assert_eq!(Category::name(), DeriveCategory::name()); + assert_eq!(Category::values(), DeriveCategory::values()); } #[test] fn active_enum_derive_signed_integers() { macro_rules! test_int { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, DeriveActiveEnum)] + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] @@ -241,7 +269,7 @@ mod tests { fn active_enum_derive_unsigned_integers() { macro_rules! test_uint { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { - #[derive(Debug, PartialEq, DeriveActiveEnum)] + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { #[sea_orm(num_value = 1)] diff --git a/tests/common/features/active_enum.rs b/tests/common/features/active_enum.rs index f152f37c..5285c5d9 100644 --- a/tests/common/features/active_enum.rs +++ b/tests/common/features/active_enum.rs @@ -15,7 +15,7 @@ pub enum Relation {} impl ActiveModelBehavior for ActiveModel {} -#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = "String", db_type = "String(Some(1))")] pub enum Category { #[sea_orm(string_value = "B")] @@ -24,7 +24,7 @@ pub enum Category { Small, } -#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] +#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = "i32", db_type = r#"Integer"#)] pub enum Color { #[sea_orm(num_value = 0)] @@ -33,11 +33,8 @@ pub enum Color { White, } -#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)] -#[sea_orm( - rs_type = "String", - db_type = r#"Enum("tea".to_owned(), vec!["EverydayTea".to_owned(), "BreakfastTea".to_owned()])"# -)] +#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)] +#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")] pub enum Tea { #[sea_orm(string_value = "EverydayTea")] EverydayTea,