Skip to content

Commit 9308e42

Browse files
committed
derive: negative discriminant support for FromValue on enums
1 parent d36f989 commit 9308e42

File tree

3 files changed

+154
-74
lines changed

3 files changed

+154
-74
lines changed

derive/src/from_value/enums/misc.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ops::Neg;
2+
13
use num_bigint::BigInt;
24
use syn::spanned::Spanned;
35

@@ -12,6 +14,13 @@ pub fn get_discriminant(def: &syn::Expr) -> Result<BigInt, crate::Error> {
1214
..
1315
}) => Ok(x.base10_parse().unwrap()),
1416
syn::Expr::Group(syn::ExprGroup { ref expr, .. }) => get_discriminant(expr),
17+
syn::Expr::Unary(x) => {
18+
let val = get_discriminant(&x.expr)?;
19+
match x.op {
20+
syn::UnOp::Neg(_) => Ok(val.neg()),
21+
_ => Err(crate::Error::UnsupportedDiscriminant(def.span())),
22+
}
23+
}
1524
expr => Err(crate::Error::UnsupportedDiscriminant(expr.span())),
1625
}
1726
}

derive/src/from_value/enums/mod.rs

+94-71
Original file line numberDiff line numberDiff line change
@@ -77,83 +77,86 @@ pub fn impl_from_value_for_enum(
7777

7878
next_discriminant = &discriminant + BigInt::from(1u8);
7979

80-
if discriminant < BigInt::default() || discriminant > BigInt::from(u16::MAX) {
81-
if !item_attrs.allow_invalid_discriminants
82-
&& !variant_attrs.allow_invalid_discriminants
83-
&& !*item_attrs.is_integer
84-
&& !*item_attrs.is_string
80+
if !*item_attrs.is_string && !*item_attrs.is_integer {
81+
if !item_attrs.allow_invalid_discriminants && !variant_attrs.allow_invalid_discriminants
8582
{
86-
crate::warn::print_warning(
87-
"negative discriminants for MySql enums are discouraging",
88-
format!("#[mysql(allow_invalid_discriminants)]\nenum {} {{", ident),
89-
"use the following annotation to suppress this warning",
90-
)
91-
.unwrap();
83+
if discriminant < BigInt::default() {
84+
crate::warn::print_warning(
85+
"negative discriminants for MySql enums are discouraging",
86+
format!("#[mysql(allow_invalid_discriminants)]\nenum {} {{", ident),
87+
"use the following annotation to suppress this warning",
88+
)
89+
.unwrap();
90+
} else if discriminant > BigInt::from(u16::MAX) {
91+
crate::warn::print_warning(
92+
"MySql only supports up to 65535 distinct elements in an enum",
93+
format!("#[mysql(allow_invalid_discriminants)]\nenum {} {{", ident),
94+
"use the following annotation to suppress this warning",
95+
)
96+
.unwrap();
97+
}
9298
}
93-
} else if discriminant == BigInt::default()
94-
&& !*item_attrs.is_integer
95-
&& !*item_attrs.is_string
96-
{
97-
if variant_attrs.explicit_invalid {
98-
variant_attrs.rename = Some("".into());
99-
} else {
100-
abort!(crate::Error::ExplicitInvalid(variant.span()))
99+
if discriminant == BigInt::default() {
100+
if variant_attrs.explicit_invalid {
101+
variant_attrs.rename = Some("".into());
102+
} else {
103+
abort!(crate::Error::ExplicitInvalid(variant.span()))
104+
}
101105
}
102-
} else {
103-
variants.push(EnumVariant {
104-
my_attrs: variant_attrs,
105-
ident: variant.ident.clone(),
106-
name: variant.ident.to_string(),
107-
discriminant,
108-
})
109106
}
107+
108+
variants.push(EnumVariant {
109+
my_attrs: variant_attrs,
110+
ident: variant.ident.clone(),
111+
name: variant.ident.to_string(),
112+
discriminant,
113+
});
110114
}
111115

112-
if !item_attrs.allow_sparse_enum && !*item_attrs.is_integer && !*item_attrs.is_string {
113-
variants.sort_by_key(|x| x.discriminant.clone());
114-
let mut prev_discriminant = BigInt::default();
115-
for variant in variants.iter() {
116-
let is_next = (variant.discriminant.clone() - BigInt::from(1_u8)) == prev_discriminant;
117-
let is_invalid =
118-
variant.discriminant == prev_discriminant && prev_discriminant == BigInt::default();
119-
if !is_next && !is_invalid {
120-
crate::warn::print_warning(
116+
if !*item_attrs.is_integer && !*item_attrs.is_string {
117+
if !item_attrs.allow_sparse_enum {
118+
variants.sort_by_key(|x| x.discriminant.clone());
119+
let mut prev_discriminant = BigInt::default();
120+
for variant in variants.iter() {
121+
let is_next =
122+
(variant.discriminant.clone() - BigInt::from(1_u8)) == prev_discriminant;
123+
let is_invalid = variant.discriminant == prev_discriminant
124+
&& prev_discriminant == BigInt::default();
125+
if !is_next && !is_invalid {
126+
crate::warn::print_warning(
121127
format!("Sparse enum variant {}::{}. Consider annotating with #[mysql(is_integer)] or #[mysql(is_string)].", ident, variant.ident),
122128
format!("#[mysql(is_integer)]\nenum {} {{", ident),
123129
"use #[mysql(allow_sparse_enum)] to suppress this warning",
124130
)
125131
.unwrap();
132+
}
133+
prev_discriminant = variant.discriminant.clone();
126134
}
127-
prev_discriminant = variant.discriminant.clone();
128135
}
129-
}
130136

131-
if min_discriminant >= BigInt::default()
132-
&& max_discriminant <= BigInt::from(u8::MAX)
133-
&& !*item_attrs.is_integer
134-
&& !*item_attrs.is_string
135-
&& !item_attrs.allow_suboptimal_repr
136-
{
137-
if !matches!(repr.0, EnumRepr::U8(_)) {
137+
if min_discriminant >= BigInt::default()
138+
&& max_discriminant <= BigInt::from(u8::MAX)
139+
&& !item_attrs.allow_suboptimal_repr
140+
{
141+
if !matches!(repr.0, EnumRepr::U8(_)) {
142+
crate::warn::print_warning(
143+
"enum representation is suboptimal. Consider the following annotation:",
144+
format!("#[repr(u8)]\nenum {} {{", ident),
145+
"use #[mysql(allow_suboptimal_repr)] to suppress this warning",
146+
)
147+
.unwrap();
148+
}
149+
} else if min_discriminant >= BigInt::default()
150+
&& max_discriminant <= BigInt::from(u16::MAX)
151+
&& !matches!(repr.0, EnumRepr::U8(_))
152+
{
138153
crate::warn::print_warning(
139154
"enum representation is suboptimal. Consider the following annotation:",
140-
format!("#[repr(u8)]\nenum {} {{", ident),
155+
format!("#[repr(u16)]\nenum {} {{", ident),
141156
"use #[mysql(allow_suboptimal_repr)] to suppress this warning",
142157
)
143158
.unwrap();
144159
}
145-
} else if min_discriminant >= BigInt::default()
146-
&& max_discriminant <= BigInt::from(u16::MAX)
147-
&& !*item_attrs.is_integer
148-
&& !*item_attrs.is_string
149-
&& !matches!(repr.0, EnumRepr::U8(_))
150-
{
151-
crate::warn::print_warning(
152-
"enum representation is suboptimal. Consider the following annotation:",
153-
format!("#[repr(u16)]\nenum {} {{", ident),
154-
"use #[mysql(allow_suboptimal_repr)] to suppress this warning",
155-
)
156-
.unwrap();
157160
}
158161

159162
let derived = Enum {
@@ -163,7 +166,8 @@ pub fn impl_from_value_for_enum(
163166
repr: repr.0,
164167
};
165168

166-
Ok(quote::quote! { #derived })
169+
let generated = quote::quote! { #derived };
170+
Ok(generated)
167171
}
168172

169173
struct Enum {
@@ -210,26 +214,45 @@ impl ToTokens for Enum {
210214
let n = syn::LitInt::new(&discriminant.to_string(), Span::call_site());
211215

212216
if *item_attrs.is_integer {
213-
quote::quote!(
214-
#crat::Value::Int(#n) | #crat::Value::UInt(#n) => {
215-
Ok(#ir_name(#parsed_name::Ready(#container_name::#ident)))
216-
}
217-
)
217+
if discriminant < &BigInt::default() {
218+
quote::quote!(
219+
#crat::Value::Int(#n) => {
220+
Ok(#ir_name(#parsed_name::Ready(#container_name::#ident)))
221+
}
222+
)
223+
} else {
224+
quote::quote!(
225+
#crat::Value::Int(#n) | #crat::Value::UInt(#n) => {
226+
Ok(#ir_name(#parsed_name::Ready(#container_name::#ident)))
227+
}
228+
)
229+
}
218230
} else if *item_attrs.is_string {
219231
quote::quote!(
220232
#crat::Value::Bytes(ref x) if x == #s => {
221233
Ok(#ir_name(#parsed_name::Parsed(#container_name::#ident, v)))
222234
}
223235
)
224236
} else {
225-
quote::quote!(
226-
#crat::Value::Bytes(ref x) if x == #s => {
227-
Ok(#ir_name(#parsed_name::Parsed(#container_name::#ident, v)))
228-
}
229-
#crat::Value::Int(#n) | #crat::Value::UInt(#n) => {
230-
Ok(#ir_name(#parsed_name::Ready(#container_name::#ident)))
231-
}
232-
)
237+
if discriminant < &BigInt::default() {
238+
quote::quote!(
239+
#crat::Value::Bytes(ref x) if x == #s => {
240+
Ok(#ir_name(#parsed_name::Parsed(#container_name::#ident, v)))
241+
}
242+
#crat::Value::Int(#n) => {
243+
Ok(#ir_name(#parsed_name::Ready(#container_name::#ident)))
244+
}
245+
)
246+
} else {
247+
quote::quote!(
248+
#crat::Value::Bytes(ref x) if x == #s => {
249+
Ok(#ir_name(#parsed_name::Parsed(#container_name::#ident, v)))
250+
}
251+
#crat::Value::Int(#n) | #crat::Value::UInt(#n) => {
252+
Ok(#ir_name(#parsed_name::Ready(#container_name::#ident)))
253+
}
254+
)
255+
}
233256
}
234257
},
235258
);

src/lib.rs

+51-3
Original file line numberDiff line numberDiff line change
@@ -579,15 +579,63 @@ fn from_value_is_string() {
579579
use crate::{prelude::FromValue, Value};
580580
#[derive(FromValue, Debug, Eq, PartialEq)]
581581
#[mysql(is_string, rename_all = "snake_case")]
582-
enum SomeType {
582+
enum SomeTypeIsString {
583583
FirstVariant = 0,
584584
SecondVariant = 2,
585585
ThirdVariant = 3,
586586
}
587587

588588
let value = Value::Bytes(b"first_variant".to_vec());
589-
assert_eq!(SomeType::FirstVariant, SomeType::from_value(value));
589+
assert_eq!(
590+
SomeTypeIsString::FirstVariant,
591+
SomeTypeIsString::from_value(value)
592+
);
590593

591594
let value = Value::Bytes(b"third_variant".to_vec());
592-
assert_eq!(SomeType::ThirdVariant, SomeType::from_value(value));
595+
assert_eq!(
596+
SomeTypeIsString::ThirdVariant,
597+
SomeTypeIsString::from_value(value)
598+
);
599+
600+
assert_eq!(
601+
Value::from(SomeTypeIsString::FirstVariant),
602+
Value::Bytes(b"first_variant".to_vec())
603+
);
604+
assert_eq!(
605+
Value::from(SomeTypeIsString::SecondVariant),
606+
Value::Bytes(b"second_variant".to_vec())
607+
);
608+
assert_eq!(
609+
Value::from(SomeTypeIsString::ThirdVariant),
610+
Value::Bytes(b"third_variant".to_vec())
611+
);
612+
}
613+
614+
#[test]
615+
fn from_value_is_integer() {
616+
use crate::{prelude::FromValue, Value};
617+
#[derive(FromValue, Debug, Eq, PartialEq)]
618+
#[mysql(is_integer, rename_all = "snake_case")]
619+
#[repr(i8)]
620+
enum SomeTypeIsInteger {
621+
FirstVariant = -1_i8,
622+
SecondVariant = 2,
623+
ThirdVariant = 3,
624+
}
625+
626+
let value = Value::Int(-1);
627+
assert_eq!(
628+
SomeTypeIsInteger::FirstVariant,
629+
SomeTypeIsInteger::from_value(value)
630+
);
631+
632+
let value = Value::Int(3);
633+
assert_eq!(
634+
SomeTypeIsInteger::ThirdVariant,
635+
SomeTypeIsInteger::from_value(value)
636+
);
637+
638+
assert_eq!(Value::from(SomeTypeIsInteger::FirstVariant), Value::Int(-1));
639+
assert_eq!(Value::from(SomeTypeIsInteger::SecondVariant), Value::Int(2));
640+
assert_eq!(Value::from(SomeTypeIsInteger::ThirdVariant), Value::Int(3));
593641
}

0 commit comments

Comments
 (0)