use chrono::{DateTime, Utc};
use crate::audit::Audit;
use crate::backend::{AuditQuery, Backend};
use crate::changes::ValueMap;
use crate::config::{self, AuditOptions};
use crate::engine;
use crate::error::Result;
use crate::id::AuditId;
use crate::query::AuditQueryBuilder;
use crate::revision::{self, Revision};
#[allow(async_fn_in_trait)]
pub trait Auditable: Send + Sync {
fn auditable_type() -> &'static str
where
Self: Sized;
fn auditable_id(&self) -> AuditId;
fn audited_attributes(&self) -> ValueMap;
fn audit_options() -> AuditOptions
where
Self: Sized;
fn primary_key() -> &'static str
where
Self: Sized,
{
"id"
}
fn inheritance_column() -> Option<&'static str>
where
Self: Sized,
{
None
}
fn audit_associated(&self) -> Option<(String, AuditId)> {
None
}
fn audit_if(&self) -> bool {
true
}
fn audit_unless(&self) -> bool {
false
}
async fn audited_create(&self, backend: &dyn Backend) -> Result<Option<Audit>>
where
Self: Sized,
{
let options = Self::audit_options();
engine::create(
backend,
&engine::meta_for(self, &options),
self.audited_attributes(),
None,
)
.await
}
async fn audited_create_with_comment(
&self,
backend: &dyn Backend,
comment: impl Into<String> + Send,
) -> Result<Option<Audit>>
where
Self: Sized,
{
let options = Self::audit_options();
engine::create(
backend,
&engine::meta_for(self, &options),
self.audited_attributes(),
Some(comment.into()),
)
.await
}
async fn audited_update(&self, backend: &dyn Backend, old: &Self) -> Result<Option<Audit>>
where
Self: Sized,
{
let options = Self::audit_options();
engine::update(
backend,
&engine::meta_for(self, &options),
old.audited_attributes(),
self.audited_attributes(),
None,
)
.await
}
async fn audited_update_with_comment(
&self,
backend: &dyn Backend,
old: &Self,
comment: impl Into<String> + Send,
) -> Result<Option<Audit>>
where
Self: Sized,
{
let options = Self::audit_options();
engine::update(
backend,
&engine::meta_for(self, &options),
old.audited_attributes(),
self.audited_attributes(),
Some(comment.into()),
)
.await
}
async fn audited_destroy(&self, backend: &dyn Backend) -> Result<Option<Audit>>
where
Self: Sized,
{
let options = Self::audit_options();
engine::destroy(
backend,
&engine::meta_for(self, &options),
self.audited_attributes(),
None,
)
.await
}
async fn audited_destroy_with_comment(
&self,
backend: &dyn Backend,
comment: impl Into<String> + Send,
) -> Result<Option<Audit>>
where
Self: Sized,
{
let options = Self::audit_options();
engine::destroy(
backend,
&engine::meta_for(self, &options),
self.audited_attributes(),
Some(comment.into()),
)
.await
}
async fn audits(backend: &dyn Backend, id: impl Into<AuditId> + Send) -> Result<Vec<Audit>>
where
Self: Sized,
{
let q = AuditQuery::default();
backend
.audits_for_auditable(Self::auditable_type(), &id.into(), &q)
.await
}
fn query(backend: &dyn Backend, id: impl Into<AuditId>) -> AuditQueryBuilder<'_>
where
Self: Sized,
{
AuditQueryBuilder::new(
backend,
Self::auditable_type().to_string(),
id.into(),
false,
)
}
fn associated_query(backend: &dyn Backend, id: impl Into<AuditId>) -> AuditQueryBuilder<'_>
where
Self: Sized,
{
AuditQueryBuilder::new(backend, Self::auditable_type().to_string(), id.into(), true)
}
async fn associated_audits(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
) -> Result<Vec<Audit>>
where
Self: Sized,
{
Self::associated_query(backend, id).fetch().await
}
async fn own_and_associated_audits(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
) -> Result<Vec<Audit>>
where
Self: Sized,
{
backend
.own_and_associated_audits(Self::auditable_type(), &id.into())
.await
}
async fn revisions(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
) -> Result<Vec<Revision>>
where
Self: Sized,
{
Self::revisions_from(backend, id, 1).await
}
async fn revisions_from(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
from_version: i32,
) -> Result<Vec<Revision>>
where
Self: Sized,
{
let all = Self::audits(backend, id).await?;
Ok(revision::revisions(&all, from_version))
}
async fn revision(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
version: i32,
) -> Result<Option<Revision>>
where
Self: Sized,
{
let all = Self::audits(backend, id).await?;
Ok(revision::revision_at_version(&all, version))
}
async fn revision_previous(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
) -> Result<Option<Revision>>
where
Self: Sized,
{
let all = Self::audits(backend, id).await?;
Ok(revision::revision_previous(&all))
}
async fn revision_at(
backend: &dyn Backend,
id: impl Into<AuditId> + Send,
time: DateTime<Utc>,
) -> Result<Option<Revision>>
where
Self: Sized,
{
let q = AuditQuery {
up_until: Some(time),
..Default::default()
};
let audits = backend
.audits_for_auditable(Self::auditable_type(), &id.into(), &q)
.await?;
Ok(revision::revision_up_until(&audits))
}
fn disable_auditing()
where
Self: Sized,
{
config::set_type_enabled(Self::auditable_type(), false);
}
fn enable_auditing()
where
Self: Sized,
{
config::set_type_enabled(Self::auditable_type(), true);
}
fn auditing_enabled() -> bool
where
Self: Sized,
{
config::auditing_enabled() && config::type_enabled(Self::auditable_type())
}
}