use proc_macro2::TokenStream;
use quote::quote;
use crate::{entity::parse::EntityDef, utils::marker};
pub fn generate(entity: &EntityDef) -> TokenStream {
let vis = &entity.vis;
let entity_name = entity.name();
let policy_trait = entity.ident_with("", "Policy");
let repo_trait = entity.ident_with("", "Repository");
let wrapper_name = entity.ident_with("", "PolicyRepository");
let marker = marker::generated();
let create_dto = entity.ident_with("Create", "Request");
let update_dto = entity.ident_with("Update", "Request");
let id_type = entity.id_field().ty();
let doc = format!(
"Repository wrapper for [`{entity_name}`] with authorization checks.\n\n\
Wraps any [`{repo_trait}`] implementation and enforces policy before operations."
);
quote! {
#marker
#[doc = #doc]
#vis struct #wrapper_name<R, P>
where
R: #repo_trait,
P: #policy_trait,
{
repo: R,
policy: P,
}
impl<R, P> #wrapper_name<R, P>
where
R: #repo_trait,
P: #policy_trait,
{
pub fn new(repo: R, policy: P) -> Self {
Self { repo, policy }
}
pub fn inner(&self) -> &R {
&self.repo
}
pub fn policy(&self) -> &P {
&self.policy
}
pub async fn create(
&self,
dto: #create_dto,
ctx: &P::Context,
) -> Result<#entity_name, ::entity_core::policy::PolicyError<R::Error, P::Error>> {
self.policy
.can_create(&dto, ctx)
.await
.map_err(::entity_core::policy::PolicyError::Policy)?;
self.repo
.create(dto)
.await
.map_err(::entity_core::policy::PolicyError::Repository)
}
pub async fn find_by_id(
&self,
id: #id_type,
ctx: &P::Context,
) -> Result<Option<#entity_name>, ::entity_core::policy::PolicyError<R::Error, P::Error>>
{
self.policy
.can_read(&id, ctx)
.await
.map_err(::entity_core::policy::PolicyError::Policy)?;
self.repo
.find_by_id(id)
.await
.map_err(::entity_core::policy::PolicyError::Repository)
}
pub async fn update(
&self,
id: #id_type,
dto: #update_dto,
ctx: &P::Context,
) -> Result<#entity_name, ::entity_core::policy::PolicyError<R::Error, P::Error>> {
self.policy
.can_update(&id, &dto, ctx)
.await
.map_err(::entity_core::policy::PolicyError::Policy)?;
self.repo
.update(id, dto)
.await
.map_err(::entity_core::policy::PolicyError::Repository)
}
pub async fn delete(
&self,
id: #id_type,
ctx: &P::Context,
) -> Result<bool, ::entity_core::policy::PolicyError<R::Error, P::Error>> {
self.policy
.can_delete(&id, ctx)
.await
.map_err(::entity_core::policy::PolicyError::Policy)?;
self.repo
.delete(id)
.await
.map_err(::entity_core::policy::PolicyError::Repository)
}
pub async fn list(
&self,
limit: i64,
offset: i64,
ctx: &P::Context,
) -> Result<Vec<#entity_name>, ::entity_core::policy::PolicyError<R::Error, P::Error>>
{
self.policy
.can_list(ctx)
.await
.map_err(::entity_core::policy::PolicyError::Policy)?;
self.repo
.list(limit, offset)
.await
.map_err(::entity_core::policy::PolicyError::Repository)
}
}
}
}