Parse enum from repr[X] enums with values (#769)

* parse enum from repr[X] enums with values

* fix parsing negative enums variants with repr[X]

* add tests for enum num_value fallback support
This commit is contained in:
Mateusz 2022-07-02 08:31:16 +02:00 committed by GitHub
parent 5ff6063fe1
commit 087f8462a6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 99 additions and 16 deletions

View File

@ -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 {

View File

@ -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);
}
}