More ergonomic DeriveActiveEnum
derive macro
This commit is contained in:
parent
2f7c9ccda7
commit
bb78a1d709
@ -1,3 +1,4 @@
|
|||||||
|
use heck::CamelCase;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{quote, quote_spanned};
|
use quote::{quote, quote_spanned};
|
||||||
use syn::{punctuated::Punctuated, token::Comma, Lit, LitInt, LitStr, Meta};
|
use syn::{punctuated::Punctuated, token::Comma, Lit, LitInt, LitStr, Meta};
|
||||||
@ -10,6 +11,7 @@ enum Error {
|
|||||||
|
|
||||||
struct ActiveEnum {
|
struct ActiveEnum {
|
||||||
ident: syn::Ident,
|
ident: syn::Ident,
|
||||||
|
enum_name: String,
|
||||||
rs_type: TokenStream,
|
rs_type: TokenStream,
|
||||||
db_type: TokenStream,
|
db_type: TokenStream,
|
||||||
is_string: bool,
|
is_string: bool,
|
||||||
@ -27,6 +29,7 @@ impl ActiveEnum {
|
|||||||
let ident_span = input.ident.span();
|
let ident_span = input.ident.span();
|
||||||
let ident = input.ident;
|
let ident = input.ident;
|
||||||
|
|
||||||
|
let mut enum_name = ident.to_string().to_camel_case();
|
||||||
let mut rs_type = Err(Error::TT(quote_spanned! {
|
let mut rs_type = Err(Error::TT(quote_spanned! {
|
||||||
ident_span => compile_error!("Missing macro attribute `rs_type`");
|
ident_span => compile_error!("Missing macro attribute `rs_type`");
|
||||||
}));
|
}));
|
||||||
@ -52,11 +55,25 @@ impl ActiveEnum {
|
|||||||
}
|
}
|
||||||
} else if name == "db_type" {
|
} else if name == "db_type" {
|
||||||
if let Lit::Str(litstr) = &nv.lit {
|
if let Lit::Str(litstr) = &nv.lit {
|
||||||
db_type = syn::parse_str::<TokenStream>(&litstr.value())
|
let s = litstr.value();
|
||||||
|
match s.as_ref() {
|
||||||
|
"Enum" => {
|
||||||
|
db_type = Ok(quote! {
|
||||||
|
Enum(Self::name(), Self::values())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
db_type = syn::parse_str::<TokenStream>(&s)
|
||||||
.map_err(Error::Syn);
|
.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 {
|
Ok(ActiveEnum {
|
||||||
ident,
|
ident,
|
||||||
|
enum_name,
|
||||||
rs_type: rs_type?,
|
rs_type: rs_type?,
|
||||||
db_type: db_type?,
|
db_type: db_type?,
|
||||||
is_string,
|
is_string,
|
||||||
@ -141,6 +159,7 @@ impl ActiveEnum {
|
|||||||
fn impl_active_enum(&self) -> TokenStream {
|
fn impl_active_enum(&self) -> TokenStream {
|
||||||
let Self {
|
let Self {
|
||||||
ident,
|
ident,
|
||||||
|
enum_name,
|
||||||
rs_type,
|
rs_type,
|
||||||
db_type,
|
db_type,
|
||||||
is_string,
|
is_string,
|
||||||
@ -181,6 +200,10 @@ impl ActiveEnum {
|
|||||||
impl sea_orm::ActiveEnum for #ident {
|
impl sea_orm::ActiveEnum for #ident {
|
||||||
type Value = #rs_type;
|
type Value = #rs_type;
|
||||||
|
|
||||||
|
fn name() -> String {
|
||||||
|
#enum_name.to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
fn to_value(&self) -> Self::Value {
|
fn to_value(&self) -> Self::Value {
|
||||||
match self {
|
match self {
|
||||||
#( Self::#variant_idents => #variant_values, )*
|
#( Self::#variant_idents => #variant_values, )*
|
||||||
|
@ -509,6 +509,9 @@ pub fn derive_active_model_behavior(input: TokenStream) -> TokenStream {
|
|||||||
/// - `db_type`: Define `ColumnType` returned by `ActiveEnum::db_type()`
|
/// - `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`
|
/// - 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"`
|
/// - 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
|
/// - For enum variant
|
||||||
/// - `string_value` or `num_value`:
|
/// - `string_value` or `num_value`:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{ColumnDef, DbErr, TryGetable};
|
use crate::{ColumnDef, DbErr, Iterable, TryGetable};
|
||||||
use sea_query::{Nullable, Value, ValueType};
|
use sea_query::{Nullable, Value, ValueType};
|
||||||
|
|
||||||
/// A Rust representation of enum defined in database.
|
/// A Rust representation of enum defined in database.
|
||||||
@ -17,8 +17,12 @@ use sea_query::{Nullable, Value, ValueType};
|
|||||||
/// use sea_orm::entity::prelude::*;
|
/// use sea_orm::entity::prelude::*;
|
||||||
///
|
///
|
||||||
/// // Using the derive macro
|
/// // Using the derive macro
|
||||||
/// #[derive(Debug, PartialEq, DeriveActiveEnum)]
|
/// #[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)]
|
||||||
/// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")]
|
/// #[sea_orm(
|
||||||
|
/// rs_type = "String",
|
||||||
|
/// db_type = "String(Some(1))",
|
||||||
|
/// enum_name = "category"
|
||||||
|
/// )]
|
||||||
/// pub enum DeriveCategory {
|
/// pub enum DeriveCategory {
|
||||||
/// #[sea_orm(string_value = "B")]
|
/// #[sea_orm(string_value = "B")]
|
||||||
/// Big,
|
/// Big,
|
||||||
@ -27,7 +31,7 @@ use sea_query::{Nullable, Value, ValueType};
|
|||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // Implementing it manually
|
/// // Implementing it manually
|
||||||
/// #[derive(Debug, PartialEq)]
|
/// #[derive(Debug, PartialEq, EnumIter)]
|
||||||
/// pub enum Category {
|
/// pub enum Category {
|
||||||
/// Big,
|
/// Big,
|
||||||
/// Small,
|
/// Small,
|
||||||
@ -38,6 +42,11 @@ use sea_query::{Nullable, Value, ValueType};
|
|||||||
/// type Value = String;
|
/// type Value = String;
|
||||||
///
|
///
|
||||||
/// // Will be atomically generated by `DeriveActiveEnum`
|
/// // Will be atomically generated by `DeriveActiveEnum`
|
||||||
|
/// fn name() -> String {
|
||||||
|
/// "category".to_owned()
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Will be atomically generated by `DeriveActiveEnum`
|
||||||
/// fn to_value(&self) -> Self::Value {
|
/// fn to_value(&self) -> Self::Value {
|
||||||
/// match self {
|
/// match self {
|
||||||
/// Self::Big => "B",
|
/// Self::Big => "B",
|
||||||
@ -71,7 +80,7 @@ use sea_query::{Nullable, Value, ValueType};
|
|||||||
/// use sea_orm::entity::prelude::*;
|
/// use sea_orm::entity::prelude::*;
|
||||||
///
|
///
|
||||||
/// // Define the `Category` active enum
|
/// // 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))")]
|
/// #[sea_orm(rs_type = "String", db_type = "String(Some(1))")]
|
||||||
/// pub enum Category {
|
/// pub enum Category {
|
||||||
/// #[sea_orm(string_value = "B")]
|
/// #[sea_orm(string_value = "B")]
|
||||||
@ -95,10 +104,13 @@ use sea_query::{Nullable, Value, ValueType};
|
|||||||
///
|
///
|
||||||
/// impl ActiveModelBehavior for ActiveModel {}
|
/// impl ActiveModelBehavior for ActiveModel {}
|
||||||
/// ```
|
/// ```
|
||||||
pub trait ActiveEnum: Sized {
|
pub trait ActiveEnum: Sized + Iterable {
|
||||||
/// Define the Rust type that each enum variant represents.
|
/// Define the Rust type that each enum variant represents.
|
||||||
type Value: Into<Value> + ValueType + Nullable + TryGetable;
|
type Value: Into<Value> + ValueType + Nullable + TryGetable;
|
||||||
|
|
||||||
|
/// Get the name of enum
|
||||||
|
fn name() -> String;
|
||||||
|
|
||||||
/// Convert enum variant into the corresponding value.
|
/// Convert enum variant into the corresponding value.
|
||||||
fn to_value(&self) -> Self::Value;
|
fn to_value(&self) -> Self::Value;
|
||||||
|
|
||||||
@ -107,6 +119,11 @@ pub trait ActiveEnum: Sized {
|
|||||||
|
|
||||||
/// Get the database column definition of this active enum.
|
/// Get the database column definition of this active enum.
|
||||||
fn db_type() -> ColumnDef;
|
fn db_type() -> ColumnDef;
|
||||||
|
|
||||||
|
/// Get the name of all enum variants
|
||||||
|
fn values() -> Vec<Self::Value> {
|
||||||
|
Self::iter().map(|s| s.to_value()).collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -117,7 +134,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn active_enum_string() {
|
fn active_enum_string() {
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, EnumIter)]
|
||||||
pub enum Category {
|
pub enum Category {
|
||||||
Big,
|
Big,
|
||||||
Small,
|
Small,
|
||||||
@ -126,6 +143,10 @@ mod tests {
|
|||||||
impl ActiveEnum for Category {
|
impl ActiveEnum for Category {
|
||||||
type Value = String;
|
type Value = String;
|
||||||
|
|
||||||
|
fn name() -> String {
|
||||||
|
"category".to_owned()
|
||||||
|
}
|
||||||
|
|
||||||
fn to_value(&self) -> Self::Value {
|
fn to_value(&self) -> Self::Value {
|
||||||
match self {
|
match self {
|
||||||
Self::Big => "B",
|
Self::Big => "B",
|
||||||
@ -150,8 +171,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, DeriveActiveEnum)]
|
#[derive(Debug, PartialEq, EnumIter, DeriveActiveEnum)]
|
||||||
#[sea_orm(rs_type = "String", db_type = "String(Some(1))")]
|
#[sea_orm(
|
||||||
|
rs_type = "String",
|
||||||
|
db_type = "String(Some(1))",
|
||||||
|
enum_name = "category"
|
||||||
|
)]
|
||||||
pub enum DeriveCategory {
|
pub enum DeriveCategory {
|
||||||
#[sea_orm(string_value = "B")]
|
#[sea_orm(string_value = "B")]
|
||||||
Big,
|
Big,
|
||||||
@ -195,13 +220,16 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(Category::db_type(), ColumnType::String(Some(1)).def());
|
assert_eq!(Category::db_type(), ColumnType::String(Some(1)).def());
|
||||||
assert_eq!(DeriveCategory::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]
|
#[test]
|
||||||
fn active_enum_derive_signed_integers() {
|
fn active_enum_derive_signed_integers() {
|
||||||
macro_rules! test_int {
|
macro_rules! test_int {
|
||||||
($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => {
|
($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)]
|
#[sea_orm(rs_type = $rs_type, db_type = $db_type)]
|
||||||
pub enum $ident {
|
pub enum $ident {
|
||||||
#[sea_orm(num_value = 1)]
|
#[sea_orm(num_value = 1)]
|
||||||
@ -241,7 +269,7 @@ mod tests {
|
|||||||
fn active_enum_derive_unsigned_integers() {
|
fn active_enum_derive_unsigned_integers() {
|
||||||
macro_rules! test_uint {
|
macro_rules! test_uint {
|
||||||
($ident: ident, $rs_type: expr, $db_type: expr, $col_def: ident) => {
|
($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)]
|
#[sea_orm(rs_type = $rs_type, db_type = $db_type)]
|
||||||
pub enum $ident {
|
pub enum $ident {
|
||||||
#[sea_orm(num_value = 1)]
|
#[sea_orm(num_value = 1)]
|
||||||
|
@ -15,7 +15,7 @@ pub enum Relation {}
|
|||||||
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
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))")]
|
#[sea_orm(rs_type = "String", db_type = "String(Some(1))")]
|
||||||
pub enum Category {
|
pub enum Category {
|
||||||
#[sea_orm(string_value = "B")]
|
#[sea_orm(string_value = "B")]
|
||||||
@ -24,7 +24,7 @@ pub enum Category {
|
|||||||
Small,
|
Small,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)]
|
#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)]
|
||||||
#[sea_orm(rs_type = "i32", db_type = r#"Integer"#)]
|
#[sea_orm(rs_type = "i32", db_type = r#"Integer"#)]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
#[sea_orm(num_value = 0)]
|
#[sea_orm(num_value = 0)]
|
||||||
@ -33,11 +33,8 @@ pub enum Color {
|
|||||||
White,
|
White,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, DeriveActiveEnum)]
|
#[derive(Debug, Clone, PartialEq, EnumIter, DeriveActiveEnum)]
|
||||||
#[sea_orm(
|
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "tea")]
|
||||||
rs_type = "String",
|
|
||||||
db_type = r#"Enum("tea".to_owned(), vec!["EverydayTea".to_owned(), "BreakfastTea".to_owned()])"#
|
|
||||||
)]
|
|
||||||
pub enum Tea {
|
pub enum Tea {
|
||||||
#[sea_orm(string_value = "EverydayTea")]
|
#[sea_orm(string_value = "EverydayTea")]
|
||||||
EverydayTea,
|
EverydayTea,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user