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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//! Event taxonomy: [`KindTag`] (wire-form discriminator) and
//! [`EventPayload`] (the trait every domain payload type implements).
//!
//! The envelope crate holds **no** central `EventKind` enum: that
//! would couple every domain to a single registry. Instead each domain
//! defines its own typed kind enum (e.g. axess's `AuthEventKind`,
//! platform governance's `GovernanceEventKind`) and projects it onto a
//! [`KindTag`] for the wire. Routing-before-decode in brokers reads
//! `KindTag`; subscribers that own the typed enum pattern-match on it
//! after decoding the payload.
use ShortString;
use fmt;
/// Wire-form event kind discriminator. A short, hashable, comparable
/// string like `"auth.login_attempt.v2"`.
///
/// Backed by [`ShortString`] (Umbra-style 16-byte stack repr): inline
/// for tags ≤ 12 bytes, refcounted heap for longer, zero-allocation
/// for `&'static str` constants. Cardinality across the org is small
/// (O(100s)); volume per kind is high. This is the workload
/// `ShortString` is built for.
;
/// Trait implemented by every domain-specific event payload enum.
///
/// The single required method is [`EventPayload::kind_tag`]: the
/// payload tells the envelope what kind it is. The envelope then
/// stores the [`KindTag`] for routing-before-decode (subscribers can
/// dispatch without deserialising the full payload).
///
/// # Implementing
///
/// Domains typically define a `<Domain>EventKind` enum with a
/// `Display` impl that yields the dotted-string form
/// (`"auth.login_attempt.v2"`), then implement this trait on the
/// payload enum:
///
/// ```ignore
/// pub enum AuthEventKind {
/// LoginAttemptV1,
/// DeviceFirstSeenV1,
/// }
/// impl core::fmt::Display for AuthEventKind { /* dotted form */ }
///
/// pub enum AuthEventPayload {
/// LoginAttempt { factor_kind: FactorKind, /* … */ },
/// DeviceFirstSeen { fingerprint_hash: FingerprintHash, /* … */ },
/// }
/// impl AuthEventPayload {
/// fn kind(&self) -> AuthEventKind { /* match self … */ }
/// }
/// impl axess_events::EventPayload for AuthEventPayload {
/// fn kind_tag(&self) -> axess_events::KindTag {
/// axess_events::KindTag::new(&self.kind().to_string())
/// }
/// #[cfg(feature = "serde")]
/// fn to_inner_json(&self) -> serde_json::Value {
/// match self {
/// Self::LoginAttempt(v) => serde_json::to_value(v).unwrap_or_default(),
/// Self::DeviceFirstSeen(v) => serde_json::to_value(v).unwrap_or_default(),
/// }
/// }
/// }
/// ```