auditlog 0.1.0

Audit trail for your data models — an ORM-agnostic core with a pluggable, async sqlx backend (SQLite & Postgres).
Documentation
//! A fluent builder for auditlog's audit scopes.

use chrono::{DateTime, Utc};

use crate::action::Action;
use crate::audit::Audit;
use crate::backend::{AuditQuery, Backend, Order};
use crate::error::Result;
use crate::id::AuditId;

/// Builds and runs an audit query for a single auditable record (or its associated audits).
///
/// Obtain one via [`crate::Auditable::query`] / [`crate::Auditable::associated_query`], chain
/// scope methods, then `.fetch()`:
///
/// ```no_run
/// # async fn demo(backend: &dyn auditlog::Backend) -> auditlog::Result<()> {
/// # use auditlog::Auditable;
/// # struct Post; #[auditlog::async_trait] impl Auditable for Post {
/// #   fn auditable_type() -> &'static str { "Post" }
/// #   fn auditable_id(&self) -> auditlog::AuditId { 1.into() }
/// #   fn audited_attributes(&self) -> auditlog::ValueMap { Default::default() }
/// #   fn audit_options() -> auditlog::AuditOptions { auditlog::AuditOptions::default() }
/// # }
/// let updates = Post::query(backend, 1).updates().descending().limit(5).fetch().await?;
/// # Ok(()) }
/// ```
pub struct AuditQueryBuilder<'a> {
    backend: &'a dyn Backend,
    type_name: String,
    id: AuditId,
    associated: bool,
    query: AuditQuery,
}

impl<'a> AuditQueryBuilder<'a> {
    pub(crate) fn new(
        backend: &'a dyn Backend,
        type_name: String,
        id: AuditId,
        associated: bool,
    ) -> Self {
        AuditQueryBuilder {
            backend,
            type_name,
            id,
            associated,
            query: AuditQuery::default(),
        }
    }

    /// Only `create` audits.
    pub fn creates(mut self) -> Self {
        self.query.action = Some(Action::Create);
        self
    }

    /// Only `update` audits.
    pub fn updates(mut self) -> Self {
        self.query.action = Some(Action::Update);
        self
    }

    /// Only `destroy` audits.
    pub fn destroys(mut self) -> Self {
        self.query.action = Some(Action::Destroy);
        self
    }

    /// `version >= from_version`.
    pub fn from_version(mut self, version: i32) -> Self {
        self.query.from_version = Some(version);
        self
    }

    /// `version <= to_version`.
    pub fn to_version(mut self, version: i32) -> Self {
        self.query.to_version = Some(version);
        self
    }

    /// `created_at <= time`.
    pub fn up_until(mut self, time: DateTime<Utc>) -> Self {
        self.query.up_until = Some(time);
        self
    }

    /// Order by `version` ascending (the default).
    pub fn ascending(mut self) -> Self {
        self.query.order = Order::VersionAsc;
        self
    }

    /// Order by `version` descending.
    pub fn descending(mut self) -> Self {
        self.query.order = Order::VersionDesc;
        self
    }

    /// Limit the number of rows.
    pub fn limit(mut self, limit: i64) -> Self {
        self.query.limit = Some(limit);
        self
    }

    /// Offset the rows.
    pub fn offset(mut self, offset: i64) -> Self {
        self.query.offset = Some(offset);
        self
    }

    /// Run the query.
    pub async fn fetch(self) -> Result<Vec<Audit>> {
        if self.associated {
            self.backend
                .audits_for_associated(&self.type_name, &self.id, &self.query)
                .await
        } else {
            self.backend
                .audits_for_auditable(&self.type_name, &self.id, &self.query)
                .await
        }
    }

    /// Run the query and return the number of matching rows.
    pub async fn count(self) -> Result<usize> {
        Ok(self.fetch().await?.len())
    }
}