Skip to main content

lunaris/
audit.rs

1//! Plan 13-01 — re-export shim for the canonical audit types that now live in
2//! `lunaris-core::audit`. This file preserves the old `use crate::audit::*`
3//! import surface so `crates/lunaris/src/forget.rs` and every downstream test
4//! keeps compiling without modification.
5//!
6//! The actual type definitions, serde shape, and `publish_audit_event` helper
7//! are at `crates/lunaris-core/src/audit.rs`. Wire-shape byte-identity is
8//! locked by the fixture-parity test at
9//! `crates/lunaris-core/tests/audit_event_fixture_parity.rs`.
10//!
11//! ## Bridge `From` impls
12//!
13//! `ForgetReceipt` (at `crates/lunaris/src/forget.rs`) uses the domain types
14//! `ForgetTarget` / `ScopeSpec` / `IndexKind`. The canonical enum in
15//! `lunaris-core::audit` uses local mirror types (`ForgetTargetData`,
16//! `ScopeSpecData`, `IndexKindData`, `ForgetReceiptData`) to stay leaf-pure.
17//! The `From<&ForgetReceipt> for ForgetReceiptData` impl below does the
18//! field-by-field map so `Lunaris::forget` can keep passing a `ForgetReceipt`
19//! to the typed helper via `.into()`.
20
21pub use lunaris_core::audit::{
22    AUDIT_TOPIC, AuditEvent, FactIdData, ForgetReceiptData, ForgetTargetData, IndexKindData,
23    PublishError, Publisher, ScopeSpecData, publish_audit_event,
24};
25
26use crate::forget::{ForgetReceipt, ForgetTarget, IndexKind, ScopeSpec};
27
28impl From<&ForgetTarget> for ForgetTargetData {
29    fn from(t: &ForgetTarget) -> Self {
30        match t {
31            ForgetTarget::Id(u) => ForgetTargetData::Id(*u),
32            ForgetTarget::Scope(s) => ForgetTargetData::Scope(s.into()),
33            ForgetTarget::Before(h) => ForgetTargetData::Before(*h),
34        }
35    }
36}
37
38impl From<&ScopeSpec> for ScopeSpecData {
39    fn from(s: &ScopeSpec) -> Self {
40        match s {
41            ScopeSpec::BySource(v) => ScopeSpecData::BySource(v.clone()),
42            ScopeSpec::ByMetadata(k, v) => ScopeSpecData::ByMetadata(k.clone(), v.clone()),
43            ScopeSpec::ByEpisode(u) => ScopeSpecData::ByEpisode(*u),
44        }
45    }
46}
47
48impl From<&IndexKind> for IndexKindData {
49    fn from(i: &IndexKind) -> Self {
50        match i {
51            IndexKind::Kv => IndexKindData::Kv,
52            IndexKind::Vector => IndexKindData::Vector,
53            IndexKind::Graph => IndexKindData::Graph,
54        }
55    }
56}
57
58impl From<&ForgetReceipt> for ForgetReceiptData {
59    fn from(r: &ForgetReceipt) -> Self {
60        Self {
61            target: (&r.target).into(),
62            indices_affected: r.indices_affected.iter().map(Into::into).collect(),
63            rows_written: r.rows_written,
64            rows_deleted: r.rows_deleted,
65            audit_lsn: r.audit_lsn,
66            preview: r.preview,
67        }
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use crate::forget::{ForgetTarget, IndexKind, ScopeSpec};
75    use lunaris_core::storage::types::Lsn;
76    use ulid::Ulid;
77
78    #[test]
79    fn re_exported_audit_topic_matches() {
80        assert_eq!(AUDIT_TOPIC, "__lunaris_audit__");
81    }
82
83    #[test]
84    fn forget_receipt_bridge_round_trips_every_target_variant() {
85        let lsn = Lsn { wall_ms: 1, counter: 2 };
86        // Id variant
87        let id = Ulid::new();
88        let r1 = ForgetReceipt {
89            target: ForgetTarget::Id(id),
90            indices_affected: vec![IndexKind::Kv, IndexKind::Vector, IndexKind::Graph],
91            matched: 7,
92            rows_written: 3,
93            rows_deleted: 4,
94            audit_lsn: lsn,
95            preview: true,
96        };
97        let d1: ForgetReceiptData = (&r1).into();
98        assert_eq!(d1.target, ForgetTargetData::Id(id));
99        assert_eq!(
100            d1.indices_affected,
101            vec![IndexKindData::Kv, IndexKindData::Vector, IndexKindData::Graph]
102        );
103        assert_eq!(d1.rows_written, 3);
104        assert_eq!(d1.rows_deleted, 4);
105        assert!(d1.preview);
106
107        // Scope::BySource
108        let r2 = ForgetReceipt {
109            target: ForgetTarget::Scope(ScopeSpec::BySource("src".into())),
110            ..r1.clone()
111        };
112        let d2: ForgetReceiptData = (&r2).into();
113        assert_eq!(d2.target, ForgetTargetData::Scope(ScopeSpecData::BySource("src".into())));
114
115        // Scope::ByMetadata
116        let r3 = ForgetReceipt {
117            target: ForgetTarget::Scope(ScopeSpec::ByMetadata("k".into(), "v".into())),
118            ..r1.clone()
119        };
120        let d3: ForgetReceiptData = (&r3).into();
121        assert_eq!(
122            d3.target,
123            ForgetTargetData::Scope(ScopeSpecData::ByMetadata("k".into(), "v".into()))
124        );
125
126        // Scope::ByEpisode
127        let r4 =
128            ForgetReceipt { target: ForgetTarget::Scope(ScopeSpec::ByEpisode(id)), ..r1.clone() };
129        let d4: ForgetReceiptData = (&r4).into();
130        assert_eq!(d4.target, ForgetTargetData::Scope(ScopeSpecData::ByEpisode(id)));
131    }
132}