use proc_macro2::TokenStream;
use quote::{ToTokens, quote, quote_spanned};
use syn::{
Data, DataEnum, DataStruct, DeriveInput, Fields, parse::Parse, parse_macro_input,
punctuated::Punctuated, spanned::Spanned,
};
#[cfg(feature = "rand")]
use geekorm_core::utils::generate_random_string;
use geekorm_core::{Columns, Table};
use values::{generate_from_value, generate_serde, generate_strings};
mod helpers;
mod tablebuilder;
mod values;
use crate::{
attr::{GeekAttribute, GeekAttributeKeys, GeekAttributeValue},
derive::{ColumnDerive, ColumnTypeDerive, ColumnTypeOptionsDerive, ColumnsDerive, TableDerive},
internal::TableState,
parsers::tablebuilder::generate_query_builder,
};
use helpers::{generate_helpers, generate_new, generate_random_helpers};
use tablebuilder::{generate_backend, generate_table_builder, generate_table_primary_key};
use self::helpers::generate_hash_helpers;
pub(crate) fn derive_parser(ast: &DeriveInput) -> Result<TokenStream, syn::Error> {
let name = &ast.ident;
let attributes = GeekAttribute::parse_all(&ast.attrs)?;
match ast.data {
Data::Struct(DataStruct {
fields: Fields::Named(ref fields),
..
}) => {
let mut errors: Vec<syn::Error> = Vec::new();
let mut columns: Vec<ColumnDerive> = Vec::new();
for field in fields.named.iter() {
match ColumnDerive::try_from(field) {
Ok(column) => columns.push(column),
Err(err) => errors.push(err),
}
}
let mut table = TableDerive {
name: name.to_string(),
columns: ColumnsDerive::from(columns),
database: None,
};
table.apply_attributes(&attributes);
TableState::add(table.clone().into());
let mut tokens = generate_struct(name, fields, &ast.generics, table)?;
if !errors.is_empty() {
for error in errors {
tokens.extend(error.to_compile_error());
}
}
Ok(tokens)
}
_ => Ok(syn::Error::new(
ast.span(),
"Table only supported derived structs with named fields",
)
.to_compile_error()),
}
}
pub(crate) fn enum_parser(ast: &DeriveInput) -> Result<TokenStream, syn::Error> {
let name = &ast.ident;
let attributes = GeekAttribute::parse_all(&ast.attrs)?;
match &ast.data {
Data::Enum(DataEnum { variants, .. }) => {
let mut tokens = TokenStream::new();
tokens.extend(generate_from_value(name, variants, &ast.generics)?);
tokens.extend(generate_serde(name, variants, &ast.generics)?);
tokens.extend(generate_strings(
name,
variants,
&ast.generics,
&attributes,
)?);
Ok(tokens)
}
_ => Ok(syn::Error::new(
ast.span(),
"Table only supported derived enums with named fields",
)
.to_compile_error()),
}
}
#[allow(unused_variables)]
fn generate_struct(
ident: &syn::Ident,
fields: &syn::FieldsNamed,
generics: &syn::Generics,
table: TableDerive,
) -> Result<TokenStream, syn::Error> {
let mut stream = TokenStream::new();
stream.extend(generate_table_builder(ident, generics, &table)?);
stream.extend(generate_query_builder(ident, generics, &table)?);
stream.extend(generate_table_primary_key(ident, generics, &table)?);
#[cfg(feature = "backends")]
{
stream.extend(generate_backend(ident, fields, generics, &table)?);
}
#[cfg(feature = "new")]
stream.extend(generate_new(ident, generics, &table));
#[cfg(feature = "helpers")]
stream.extend(generate_helpers(ident, generics, &table)?);
#[cfg(feature = "hash")]
stream.extend(generate_hash_helpers(ident, generics, &table)?);
#[cfg(feature = "rand")]
stream.extend(generate_random_helpers(ident, generics, &table)?);
Ok(stream)
}