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
159
160
use TokenStream;
use parse_macro_input;
/// Retries the annotated async function when it fails with a concurrent
/// modification error (or with any error when `any_error = true`).
///
/// Attempts are spaced with exponential backoff (25ms, 50ms, 100ms, ...
/// capped at 1s) so a contended entity is not hammered in a hot loop while
/// the conflicting writer finishes its transaction.
///
/// # Arguments
///
/// - `max_retries = N` — maximum attempts (default: 3)
/// - `any_error = true|false` — retry on any error, not just concurrent
/// modifications (default: false). Only enable this when the annotated
/// function is fully idempotent.
/// Automatically captures function arguments into the event context.
///
/// This attribute macro wraps functions to automatically insert specified arguments
/// into the current [`EventContext`](es_entity::context::EventContext), making them
/// available for audit trails when events are persisted.
///
/// # Behavior
///
/// - **For async functions**: Uses the [`WithEventContext`](es_entity::context::WithEventContext)
/// trait to propagate context across async boundaries
/// - **For sync functions**: Uses [`EventContext::fork()`](es_entity::context::EventContext::fork)
/// to create an isolated child context
///
/// # Syntax
///
/// ```rust,ignore
/// #[es_event_context] // No arguments captured
/// #[es_event_context(arg1)] // Capture single argument
/// #[es_event_context(arg1, arg2)] // Capture multiple arguments
/// ```
///
/// # Examples
///
/// ## Async function with argument capture
/// ```rust,ignore
/// use es_entity_macros::es_event_context;
///
/// impl UserService {
/// #[es_event_context(user_id, operation)]
/// async fn update_user(&self, user_id: UserId, operation: &str, data: UserData) -> Result<()> {
/// // user_id and operation are automatically added to context
/// // They will be included when events are persisted
/// self.repo.update(data).await
/// }
/// }
/// ```
///
/// ## Sync function with context isolation
/// ```rust,ignore
/// use es_entity_macros::es_event_context;
///
/// impl Calculator {
/// #[es_event_context(transaction_id)]
/// fn process(&mut self, transaction_id: u64, amount: i64) {
/// // transaction_id is captured in an isolated context
/// // Parent context is restored when function exits
/// self.apply_transaction(amount);
/// }
/// }
/// ```
///
/// ## Manual context additions
/// ```rust,ignore
/// use es_entity_macros::es_event_context;
/// use es_entity::context::EventContext;
///
/// #[es_event_context(request_id)]
/// async fn handle_request(request_id: String, data: RequestData) {
/// // request_id is automatically captured
///
/// // You can still manually add more context
/// let mut ctx = EventContext::current();
/// ctx.insert("timestamp", &chrono::Utc::now()).unwrap();
///
/// process_data(data).await;
/// }
/// ```
///
/// # Context Keys
///
/// Arguments are captured using their parameter names as keys. For example,
/// `user_id: UserId` will be stored with key `"user_id"` in the context.
///
/// # See Also
///
/// - [`EventContext`](es_entity::context::EventContext) - The context management system
/// - [`WithEventContext`](es_entity::context::WithEventContext) - Async context propagation
/// - Event Context chapter in the book for complete usage patterns