diff --git a/sea-orm-macros/src/derives/active_enum.rs b/sea-orm-macros/src/derives/active_enum.rs index d75c9748..cbc9c4b0 100644 --- a/sea-orm-macros/src/derives/active_enum.rs +++ b/sea-orm-macros/src/derives/active_enum.rs @@ -1,7 +1,7 @@ use heck::CamelCase; use proc_macro2::TokenStream; use quote::{quote, quote_spanned}; -use syn::{punctuated::Punctuated, token::Comma, Lit, LitInt, LitStr, Meta}; +use syn::{parse, punctuated::Punctuated, token::Comma, Expr, Lit, LitInt, LitStr, Meta, UnOp}; enum Error { InputNotEnum, @@ -128,9 +128,41 @@ impl ActiveEnum { } if string_value.is_none() && num_value.is_none() { - return Err(Error::TT(quote_spanned! { - variant_span => compile_error!("Missing macro attribute, either `string_value` or `num_value` should be specified"); - })); + match variant.discriminant { + Some((_, Expr::Lit(exprlit))) => { + if let Lit::Int(litint) = exprlit.lit { + is_int = true; + num_value = Some(litint); + } else { + return Err(Error::TT(quote_spanned! { + variant_span => compile_error!("Enum variant discriminant is not an integer"); + })); + } + } + //rust doesn't provide negative variants in enums as a single LitInt, this workarounds that + Some((_, Expr::Unary(exprnlit))) => { + if let UnOp::Neg(_) = exprnlit.op { + if let Expr::Lit(exprlit) = *exprnlit.expr { + if let Lit::Int(litint) = exprlit.lit { + let negative_token = quote! { -#litint }; + let litint = parse(negative_token.into()).unwrap(); + + is_int = true; + num_value = Some(litint); + } + } + } else { + return Err(Error::TT(quote_spanned! { + variant_span => compile_error!("Only - token is supported in enum variants, not ! and *"); + })); + } + } + _ => { + return Err(Error::TT(quote_spanned! { + variant_span => compile_error!("Missing macro attribute, either `string_value` or `num_value` should be specified or specify repr[X] and have a value for every entry"); + })); + } + } } variants.push(ActiveEnumVariant { diff --git a/src/entity/active_enum.rs b/src/entity/active_enum.rs index 29238941..db34eb34 100644 --- a/src/entity/active_enum.rs +++ b/src/entity/active_enum.rs @@ -232,19 +232,40 @@ mod tests { #[test] fn active_enum_derive_signed_integers() { - macro_rules! test_int { + macro_rules! test_num_value_int { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] pub enum $ident { + #[sea_orm(num_value = -10)] + Negative, #[sea_orm(num_value = 1)] Big, #[sea_orm(num_value = 0)] Small, - #[sea_orm(num_value = -10)] - Negative, } + test_int!($ident, $rs_type, $db_type, $col_def); + }; + } + + macro_rules! test_fallback_int { + ($ident: ident, $fallback_type: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[sea_orm(rs_type = $rs_type, db_type = $db_type)] + #[repr(i32)] + pub enum $ident { + Big = 1, + Small = 0, + Negative = -10, + } + + test_int!($ident, $rs_type, $db_type, $col_def); + }; + } + + macro_rules! test_int { + ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { assert_eq!($ident::Big.to_value(), 1); assert_eq!($ident::Small.to_value(), 0); assert_eq!($ident::Negative.to_value(), -10); @@ -264,15 +285,20 @@ mod tests { }; } - test_int!(I8, "i8", "TinyInteger", TinyInteger); - test_int!(I16, "i16", "SmallInteger", SmallInteger); - test_int!(I32, "i32", "Integer", Integer); - test_int!(I64, "i64", "BigInteger", BigInteger); + test_num_value_int!(I8, "i8", "TinyInteger", TinyInteger); + test_num_value_int!(I16, "i16", "SmallInteger", SmallInteger); + test_num_value_int!(I32, "i32", "Integer", Integer); + test_num_value_int!(I64, "i64", "BigInteger", BigInteger); + + test_fallback_int!(I8Fallback, i8, "i8", "TinyInteger", TinyInteger); + test_fallback_int!(I16Fallback, i16, "i16", "SmallInteger", SmallInteger); + test_fallback_int!(I32Fallback, i32, "i32", "Integer", Integer); + test_fallback_int!(I64Fallback, i64, "i64", "BigInteger", BigInteger); } #[test] fn active_enum_derive_unsigned_integers() { - macro_rules! test_uint { + macro_rules! test_num_value_uint { ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] #[sea_orm(rs_type = $rs_type, db_type = $db_type)] @@ -283,6 +309,26 @@ mod tests { Small, } + test_uint!($ident, $rs_type, $db_type, $col_def); + }; + } + + macro_rules! test_fallback_uint { + ($ident: ident, $fallback_type: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { + #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)] + #[sea_orm(rs_type = $rs_type, db_type = $db_type)] + #[repr($fallback_type)] + pub enum $ident { + Big = 1, + Small = 0, + } + + test_uint!($ident, $rs_type, $db_type, $col_def); + }; + } + + macro_rules! test_uint { + ($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => { assert_eq!($ident::Big.to_value(), 1); assert_eq!($ident::Small.to_value(), 0); @@ -300,9 +346,14 @@ mod tests { }; } - test_uint!(U8, "u8", "TinyInteger", TinyInteger); - test_uint!(U16, "u16", "SmallInteger", SmallInteger); - test_uint!(U32, "u32", "Integer", Integer); - test_uint!(U64, "u64", "BigInteger", BigInteger); + test_num_value_uint!(U8, "u8", "TinyInteger", TinyInteger); + test_num_value_uint!(U16, "u16", "SmallInteger", SmallInteger); + test_num_value_uint!(U32, "u32", "Integer", Integer); + test_num_value_uint!(U64, "u64", "BigInteger", BigInteger); + + test_fallback_uint!(U8Fallback, u8, "u8", "TinyInteger", TinyInteger); + test_fallback_uint!(U16Fallback, u16, "u16", "SmallInteger", SmallInteger); + test_fallback_uint!(U32Fallback, u32, "u32", "Integer", Integer); + test_fallback_uint!(U64Fallback, u64, "u64", "BigInteger", BigInteger); } }