use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use super::parse::EntityDef;
use crate::utils::marker;
pub fn generate(entity: &EntityDef) -> TokenStream {
if !entity.has_hooks() {
return TokenStream::new();
}
let vis = &entity.vis;
let entity_name = entity.name();
let hooks_trait = format_ident!("{}Hooks", entity_name);
let id_type = entity.id_field().ty();
let create_hooks = generate_create_hooks(entity);
let update_hooks = generate_update_hooks(entity, id_type);
let delete_hooks = generate_delete_hooks(id_type, entity.is_soft_delete());
let command_hooks = generate_command_hooks(entity);
let marker = marker::generated();
quote! {
#marker
#[async_trait::async_trait]
#vis trait #hooks_trait: Send + Sync {
type Error: std::error::Error + Send + Sync;
#create_hooks
#update_hooks
#delete_hooks
#command_hooks
}
}
}
fn generate_create_hooks(entity: &EntityDef) -> TokenStream {
if entity.create_fields().is_empty() {
return TokenStream::new();
}
let entity_name = entity.name();
let create_dto = entity.ident_with("Create", "Request");
quote! {
async fn before_create(&self, dto: &mut #create_dto) -> Result<(), Self::Error> {
let _ = dto;
Ok(())
}
async fn after_create(&self, entity: &#entity_name) -> Result<(), Self::Error> {
let _ = entity;
Ok(())
}
}
}
fn generate_update_hooks(entity: &EntityDef, id_type: &syn::Type) -> TokenStream {
if entity.update_fields().is_empty() {
return TokenStream::new();
}
let entity_name = entity.name();
let update_dto = entity.ident_with("Update", "Request");
quote! {
async fn before_update(
&self,
id: &#id_type,
dto: &mut #update_dto
) -> Result<(), Self::Error> {
let _ = (id, dto);
Ok(())
}
async fn after_update(&self, entity: &#entity_name) -> Result<(), Self::Error> {
let _ = entity;
Ok(())
}
}
}
fn generate_delete_hooks(id_type: &syn::Type, soft_delete: bool) -> TokenStream {
let soft_delete_hooks = if soft_delete {
quote! {
async fn before_hard_delete(&self, id: &#id_type) -> Result<(), Self::Error> {
let _ = id;
Ok(())
}
async fn after_hard_delete(&self, id: &#id_type) -> Result<(), Self::Error> {
let _ = id;
Ok(())
}
async fn before_restore(&self, id: &#id_type) -> Result<(), Self::Error> {
let _ = id;
Ok(())
}
async fn after_restore(&self, id: &#id_type) -> Result<(), Self::Error> {
let _ = id;
Ok(())
}
}
} else {
TokenStream::new()
};
quote! {
async fn before_delete(&self, id: &#id_type) -> Result<(), Self::Error> {
let _ = id;
Ok(())
}
async fn after_delete(&self, id: &#id_type) -> Result<(), Self::Error> {
let _ = id;
Ok(())
}
#soft_delete_hooks
}
}
fn generate_command_hooks(entity: &EntityDef) -> TokenStream {
if !entity.has_commands() || entity.command_defs().is_empty() {
return TokenStream::new();
}
let entity_name = entity.name();
let command_enum = format_ident!("{}Command", entity_name);
let result_enum = format_ident!("{}CommandResult", entity_name);
quote! {
async fn before_command(&self, cmd: &#command_enum) -> Result<(), Self::Error> {
let _ = cmd;
Ok(())
}
async fn after_command(
&self,
cmd: &#command_enum,
result: &#result_enum
) -> Result<(), Self::Error> {
let _ = (cmd, result);
Ok(())
}
}
}