-
-
Notifications
You must be signed in to change notification settings - Fork 524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
DeriveDisplay macro for enum #1726
Changes from 7 commits
1f8565e
2e19da1
6f1c8f8
283917d
f70b89a
c1976a8
d52f5a8
8be294b
cc7af88
38ccd41
9cf66d1
148b718
c85ce1d
01ab380
4a2b742
b28246a
49d1d3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
use proc_macro2::TokenStream; | ||
use quote::{quote, quote_spanned, ToTokens}; | ||
use syn::{LitInt, LitStr}; | ||
|
||
enum Error { | ||
InputNotEnum, | ||
Syn(syn::Error), | ||
TT(TokenStream), | ||
} | ||
|
||
struct Display { | ||
ident: syn::Ident, | ||
variants: Vec<DisplayVariant>, | ||
} | ||
|
||
struct DisplayVariant { | ||
ident: syn::Ident, | ||
display_value: TokenStream, | ||
} | ||
|
||
impl Display { | ||
fn new(input: syn::DeriveInput) -> Result<Self, Error> { | ||
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 { | ||
dbg!(variant.ident.clone()); | ||
let mut display_value = variant.ident.clone().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") { | ||
Some(meta.value()?.parse::<LitStr>()?); | ||
} else if meta.path.is_ident("num_value") { | ||
Some(meta.value()?.parse::<LitInt>()?); | ||
} else if meta.path.is_ident("display_value") { | ||
display_value = meta.value()?.parse::<LitStr>()?.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<TokenStream> { | ||
let expanded_impl_active_enum = self.impl_active_enum(); | ||
|
||
Ok(expanded_impl_active_enum) | ||
} | ||
|
||
fn impl_active_enum(&self) -> TokenStream { | ||
let Self { ident, variants } = self; | ||
|
||
let variant_idents: Vec<syn::Ident> = variants | ||
.iter() | ||
.map(|variant| variant.ident.clone()) | ||
.collect(); | ||
|
||
let variant_display: Vec<TokenStream> = variants | ||
.iter() | ||
.map(|variant| variant.display_value.to_owned()) | ||
.collect(); | ||
// dbg!(variant_display[0].to_string()) ; | ||
|
||
quote!( | ||
impl #ident { | ||
darkmmon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
fn to_display_value(&self) -> String { | ||
match self { | ||
#( Self::#variant_idents => #variant_display, )* | ||
} | ||
.to_owned() | ||
} | ||
} | ||
|
||
#[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 = Self::to_display_value(&self).into(); | ||
write!(f, "{}", v) | ||
} | ||
} | ||
) | ||
} | ||
} | ||
|
||
pub fn expand_derive_active_enum_display(input: syn::DeriveInput) -> syn::Result<TokenStream> { | ||
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 activeenum_Display on enums"); | ||
}), | ||
Err(Error::TT(token_stream)) => Ok(token_stream), | ||
Err(Error::Syn(e)) => Err(e), | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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'"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently the fallback enums doesn't pass this test as it weirdly doesn't have the single quotes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We expect no single quote: assert_eq!(format!("{}", Enum::Big), "Big"); |
||
}; | ||
} | ||
|
||
|
@@ -408,7 +408,7 @@ mod tests { | |
|
||
#[test] | ||
fn escaped_non_uax31() { | ||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy)] | ||
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum, Copy, DeriveDisplay)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we have a few test cases NOT deriving DeriveDisplay where it is not needed? Just want to make sure we have test coverage for both cases. |
||
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "pop_os_names_typos")] | ||
pub enum PopOSTypos { | ||
#[sea_orm(string_value = "Pop!_OS")] | ||
|
@@ -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)", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
} | ||
Comment on lines
+59
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh wait, actually, did I miss the test case for this? There are no corresponding asserts? As this is the only place the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is not needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the code for DeriveActiveEnum will panic if there is some unrecognised attributes, I added this so that deriving both Display and Active Enum would not make the code panic.