use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::quote;
use syn::{parse_macro_input, DataEnum, DataStruct, DeriveInput, Fields, Type};
pub fn derive_field_sea_value_inner(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name: Ident = input.ident;
let expanded = match input.data {
syn::Data::Enum(data) => process_enum(name, data),
syn::Data::Struct(data) => process_struct(name, data),
_ => panic!("Field can only be used with enums and tuple struct for now"),
};
TokenStream::from(expanded)
}
fn process_struct(name: Ident, data: DataStruct) -> proc_macro2::TokenStream {
let first_tuple_field = match data.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => fields.unnamed.into_iter().next().unwrap(),
_ => panic!("Expected a tuple struct with one field"),
};
let field_type = first_tuple_field.ty;
let field_type_path = match field_type {
Type::Group(group) => match *group.elem {
Type::Path(p) => p,
_ => panic!("Unsupported type... TypeGroup.elem is not a path"),
},
Type::Path(p) => p,
_ => panic!("Unsupported type... not Type::Path or Type::Group"),
};
let value_variant = match field_type_path.path.get_ident() {
Some(ident) => match ident.to_string().as_str() {
"bool" => quote! { Bool },
"i8" => quote! { TinyInt },
"i16" => quote! { SmallInt },
"i32" => quote! { Int },
"i64" => quote! { BigInt },
"u8" => quote! { TinyUnsigned },
"u16" => quote! { SmallUnsigned },
"u32" => quote! { Unsigned },
"u64" => quote! { BigUnsigned },
"f32" => quote! { Float },
"f64" => quote! { Double },
"String" => quote! { String },
"char" => quote! { Char },
_ => panic!("Unsupported type... {ident:?}"),
},
None => panic!("Unsupported type... no ident found"),
};
let expanded = quote! {
impl From<#name> for sea_query::Value {
fn from(value: #name) -> Self {
Self::#value_variant(Some(value.0))
}
}
impl sea_query::Nullable for #name {
fn null() -> sea_query::Value {
sea_query::Value::#value_variant(None)
}
}
};
expanded
}
fn process_enum(name: Ident, data_enum: DataEnum) -> proc_macro2::TokenStream {
let mut first_variant = None;
let arms = data_enum
.variants
.iter()
.map(|variant| {
let variant_name = &variant.ident;
let variant_name_str = variant_name.to_string();
if first_variant.is_none() {
first_variant = Some(variant_name.clone());
}
quote! {
#name::#variant_name => #variant_name_str.into(),
}
})
.collect::<Vec<_>>();
let expanded = quote! {
impl From<#name> for sea_query::Value {
fn from(val: #name) -> Self {
match val {
#(#arms)*
}
}
}
impl sea_query::Nullable for #name {
fn null() -> sea_query::Value {
sea_query::Value::String(None)
}
}
};
expanded
}