201 lines
5.2 KiB
Rust
201 lines
5.2 KiB
Rust
mod attr;
|
|
mod error;
|
|
pub mod iden_static;
|
|
mod path;
|
|
mod write_arm;
|
|
|
|
use darling::FromMeta;
|
|
use heck::ToSnakeCase;
|
|
use proc_macro::TokenStream;
|
|
use quote::{quote, quote_spanned};
|
|
use std::convert::{TryFrom, TryInto};
|
|
use syn::{Attribute, DataEnum, DataStruct, DeriveInput, Fields, Ident, Variant};
|
|
|
|
use attr::IdenAttr;
|
|
use error::ErrorMsg;
|
|
use path::IdenPath;
|
|
use write_arm::IdenVariant;
|
|
|
|
struct DeriveIden;
|
|
struct DeriveIdenStatic;
|
|
|
|
pub fn expand(input: DeriveInput) -> TokenStream {
|
|
let DeriveInput {
|
|
ident, data, attrs, ..
|
|
} = input;
|
|
|
|
let table_name = match get_table_name(&ident, attrs) {
|
|
Ok(v) => v,
|
|
Err(e) => return e.to_compile_error().into(),
|
|
};
|
|
|
|
// Currently we only support enums and unit structs
|
|
let variants =
|
|
match data {
|
|
syn::Data::Enum(DataEnum { variants, .. }) => variants,
|
|
syn::Data::Struct(DataStruct {
|
|
fields: Fields::Unit,
|
|
..
|
|
}) => return impl_iden_for_unit_struct(&ident, &table_name).into(),
|
|
_ => return quote_spanned! {
|
|
ident.span() => compile_error!("you can only derive Iden on enums or unit structs");
|
|
}
|
|
.into(),
|
|
};
|
|
|
|
if variants.is_empty() {
|
|
return TokenStream::new();
|
|
}
|
|
|
|
let output = impl_iden_for_enum(&ident, &table_name, variants.iter());
|
|
|
|
output.into()
|
|
}
|
|
|
|
fn impl_iden_for_unit_struct(
|
|
ident: &proc_macro2::Ident,
|
|
table_name: &str,
|
|
) -> proc_macro2::TokenStream {
|
|
let sea_query_path = sea_query_path();
|
|
|
|
if is_static_iden(table_name) {
|
|
quote! {
|
|
impl #sea_query_path::Iden for #ident {
|
|
fn quoted(&self) -> std::borrow::Cow<'static, str> {
|
|
std::borrow::Cow::Borrowed(#table_name)
|
|
}
|
|
|
|
fn unquoted(&self) -> &str {
|
|
#table_name
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
impl #sea_query_path::Iden for #ident {
|
|
fn unquoted(&self) -> &str {
|
|
#table_name
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn impl_iden_for_enum<'a, T>(
|
|
ident: &proc_macro2::Ident,
|
|
table_name: &str,
|
|
variants: T,
|
|
) -> proc_macro2::TokenStream
|
|
where
|
|
T: Iterator<Item = &'a Variant>,
|
|
{
|
|
let sea_query_path = sea_query_path();
|
|
|
|
let mut is_all_static_iden = true;
|
|
|
|
let match_arms = match variants
|
|
.map(|v| {
|
|
let v = IdenVariant::<DeriveIden>::try_from((table_name, v))?;
|
|
is_all_static_iden &= v.is_static_iden();
|
|
Ok(v)
|
|
})
|
|
.collect::<syn::Result<Vec<_>>>()
|
|
{
|
|
Ok(v) => v,
|
|
Err(e) => return e.to_compile_error(),
|
|
};
|
|
|
|
if is_all_static_iden {
|
|
quote! {
|
|
impl #sea_query_path::Iden for #ident {
|
|
fn quoted(&self) -> std::borrow::Cow<'static, str> {
|
|
std::borrow::Cow::Borrowed(match self {
|
|
#(#match_arms),*
|
|
})
|
|
}
|
|
|
|
fn unquoted(&self) -> &str {
|
|
match self {
|
|
#(#match_arms),*
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
quote! {
|
|
impl #sea_query_path::Iden for #ident {
|
|
fn unquoted(&self) -> &str {
|
|
match self {
|
|
#(#match_arms),*
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn sea_query_path() -> proc_macro2::TokenStream {
|
|
if cfg!(feature = "sea-orm") {
|
|
quote!(sea_orm::sea_query)
|
|
} else {
|
|
quote!(sea_query)
|
|
}
|
|
}
|
|
|
|
pub struct NamingHolder {
|
|
pub default: Ident,
|
|
pub pascal: Ident,
|
|
}
|
|
|
|
#[derive(Debug, FromMeta)]
|
|
pub struct GenEnumArgs {
|
|
#[darling(default)]
|
|
pub prefix: Option<String>,
|
|
#[darling(default)]
|
|
pub suffix: Option<String>,
|
|
#[darling(default)]
|
|
pub crate_name: Option<String>,
|
|
#[darling(default)]
|
|
pub table_name: Option<String>,
|
|
}
|
|
|
|
pub const DEFAULT_PREFIX: &str = "";
|
|
pub const DEFAULT_SUFFIX: &str = "Iden";
|
|
pub const DEFAULT_CRATE_NAME: &str = "sea_query";
|
|
|
|
impl Default for GenEnumArgs {
|
|
fn default() -> Self {
|
|
Self {
|
|
prefix: Some(DEFAULT_PREFIX.to_string()),
|
|
suffix: Some(DEFAULT_SUFFIX.to_string()),
|
|
crate_name: Some(DEFAULT_CRATE_NAME.to_string()),
|
|
table_name: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn get_table_name(ident: &proc_macro2::Ident, attrs: Vec<Attribute>) -> Result<String, syn::Error> {
|
|
let table_name = match find_attr(&attrs) {
|
|
Some(att) => match att.try_into()? {
|
|
IdenAttr::Rename(lit) => lit,
|
|
_ => return Err(syn::Error::new_spanned(att, ErrorMsg::ContainerAttr)),
|
|
},
|
|
None => ident.to_string().to_snake_case(),
|
|
};
|
|
Ok(table_name)
|
|
}
|
|
|
|
fn find_attr(attrs: &[Attribute]) -> Option<&Attribute> {
|
|
attrs.iter().find(|attr| {
|
|
attr.path().is_ident(&IdenPath::Iden) || attr.path().is_ident(&IdenPath::Method)
|
|
})
|
|
}
|
|
|
|
pub fn is_static_iden(name: &str) -> bool {
|
|
// can only begin with [a-z_]
|
|
name.chars()
|
|
.take(1)
|
|
.all(|c| c == '_' || c.is_ascii_alphabetic())
|
|
&& name.chars().all(|c| c == '_' || c.is_ascii_alphanumeric())
|
|
}
|