1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! `auditlog` — an audit trail for your data models.
//!
//! `auditlog` records every create / update / destroy of your models into a single polymorphic
//! `audits` table, capturing *what* changed (a diff), *who* changed it, *when*, from *where*, and an
//! optional comment — and lets you query that history and reconstruct past revisions.
//!
//! `auditlog` is **ORM-agnostic**: you implement the small [`Auditable`] trait for your model and
//! hand it a [`Backend`]. A ready-to-use async [`SqlxBackend`] (SQLite & Postgres) is included,
//! plus an in-memory [`MemoryBackend`] for tests.
//!
//! # Quick start
//!
//! ```
//! use auditlog::{Auditable, AuditOptions, AuditId, ValueMap, MemoryBackend};
//! use serde_json::json;
//!
//! struct Post { id: i64, title: String, body: String }
//!
//! impl Auditable for Post {
//! fn auditable_type() -> &'static str { "Post" }
//! fn auditable_id(&self) -> AuditId { self.id.into() }
//! fn audited_attributes(&self) -> ValueMap {
//! let mut m = ValueMap::new();
//! m.insert("id".into(), json!(self.id));
//! m.insert("title".into(), json!(self.title));
//! m.insert("body".into(), json!(self.body));
//! m
//! }
//! fn audit_options() -> AuditOptions { AuditOptions::default() }
//! }
//!
//! # async fn run() -> auditlog::Result<()> {
//! let backend = MemoryBackend::new();
//!
//! // create
//! let mut post = Post { id: 1, title: "Hello".into(), body: "...".into() };
//! post.audited_create(&backend).await?;
//!
//! // update (diff old → new)
//! let old = Post { id: 1, title: "Hello".into(), body: "...".into() };
//! post.title = "Hello, world".into();
//! post.audited_update(&backend, &old).await?;
//!
//! let audits = Post::audits(&backend, 1).await?;
//! assert_eq!(audits.len(), 2);
//! # Ok(()) }
//! # tokio::runtime::Builder::new_current_thread().build().unwrap().block_on(run()).unwrap();
//! ```
//!
//! # Who made the change
//!
//! Wrap your unit of work in an [`as_user`] scope: every audit recorded inside the scope is
//! attributed to the given actor. The acting user is read from a [`tokio::task_local`] context, so
//! it is isolated per task:
//!
//! ```no_run
//! # use auditlog::*;
//! # async fn run(backend: &dyn Backend, post: &(impl Auditable + Sized)) -> Result<()> {
//! as_user(Actor::record("User", 7), async {
//! post.audited_create(backend).await
//! }).await?;
//! # Ok(()) }
//! ```
//!
//! # Feature flags
//!
//! * `sqlite` *(default)* — the [`SqlxBackend`] on SQLite.
//! * `postgres` — the [`SqlxBackend`] on Postgres.
//!
//! See the crate's `docs/SPEC.md` for the exhaustive, code-accurate behavior specification.
pub use ;
pub use Actor;
pub use ;
pub use Auditable;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use AuditId;
pub use AuditQueryBuilder;
pub use Revision;
pub use SqlxBackend;
/// Re-export so downstream code and doc examples can reference the same `async_trait`.
pub use async_trait;