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
//! # dcontext-dactor
//!
//! Automatic dcontext propagation through [dactor](https://github.com/microsoft/dactor)
//! actor messages.
//!
//! This crate bridges [`dcontext`] (distributed context propagation) with
//! [`dactor`] (actor framework) by providing interceptors that automatically
//! carry context across actor message boundaries — **no boilerplate needed
//! in handlers**.
//!
//! ## How It Works
//!
//! Context propagation is a **two-stage interceptor pipeline**:
//!
//! 1. **Outbound interceptor** (sender side) — [`ContextOutboundInterceptor`]
//! captures the current dcontext and attaches it as message headers.
//! - Local targets → [`ContextSnapshotHeader`] (preserves local-only values)
//! - Remote targets → [`ContextHeader`] (serialized wire bytes)
//!
//! 2. **Inbound interceptor** (receiver side) — [`ContextInboundInterceptor`]
//! performs two actions:
//! - `on_receive`: normalizes headers — deserializes wire bytes into a
//! [`ContextSnapshotHeader`] if needed.
//! - `wrap_handler`: wraps the handler future with
//! [`dcontext::async_ctx::with_context`], restoring the propagated
//! snapshot into the async task-local scope automatically.
//!
//! This uses dactor 0.3's `wrap_handler` feature to wrap the handler future
//! with a task-local context scope. `dcontext::async_ctx::get_context` /
//! `dcontext::async_ctx::set_context` work transparently inside the handler.
//!
//! ## Error Handling
//!
//! Both interceptors accept an [`ErrorPolicy`] that controls behavior when
//! serialization or deserialization fails:
//!
//! - [`ErrorPolicy::LogAndContinue`] (default) — log a warning, deliver the
//! message without context.
//! - [`ErrorPolicy::Reject`] — reject the message via
//! [`Disposition::Reject`](dactor::Disposition::Reject).
//!
//! ## Local vs. Remote
//!
//! - **Local** (same process): Context is propagated via
//! [`ContextSnapshot`](dcontext::ContextSnapshot), preserving local-only
//! values that cannot be serialized. **No serialization is performed.**
//!
//! - **Remote** (cross-process): Context is serialized to bytes via
//! [`dcontext::async_ctx::serialize_context`] and transmitted as a wire
//! header.
//! Local-only values are excluded.
//!
//! ## Quick Start
//!
//! ```ignore
//! use dcontext_dactor::{ContextOutboundInterceptor, ContextInboundInterceptor};
//!
//! // Register interceptors on your runtime — that's it!
//! runtime.add_outbound_interceptor(Box::new(ContextOutboundInterceptor::default()));
//! runtime.add_inbound_interceptor(Box::new(ContextInboundInterceptor::default()));
//!
//! // Handlers automatically have dcontext available — no boilerplate
//! #[async_trait]
//! impl Handler<MyMessage> for MyActor {
//! async fn handle(&mut self, msg: MyMessage, ctx: &mut ActorContext) -> () {
//! // dcontext is automatically restored by the interceptor's wrap_handler
//! let rid: RequestId = dcontext::async_ctx::get_context("request_id").unwrap();
//! // ... handle message with context available ...
//! }
//! }
//! ```
//!
//! ## Manual Extraction
//!
//! For advanced use cases (e.g., spawning sub-tasks that need context),
//! [`extract_context`] is still available to pull the snapshot from headers.
//! [`with_propagated_context`] is retained for backward compatibility but is
//! no longer needed when using the interceptor pipeline.
pub use ;
pub use ContextInboundInterceptor;
pub use ContextOutboundInterceptor;
pub use extract_context;
pub use with_propagated_context;
/// Register dcontext header deserializers with a dactor [`HeaderRegistry`](dactor::HeaderRegistry).
///
/// Call this at startup before processing remote messages so that
/// [`ContextHeader`] can be reconstructed from wire bytes during remote
/// actor communication.
///
/// # Example
///
/// ```ignore
/// use dactor::HeaderRegistry;
/// use dcontext_dactor::register_context_headers;
///
/// let mut registry = HeaderRegistry::new();
/// register_context_headers(&mut registry);
/// // pass registry to your transport / runtime
/// ```
/// Controls how interceptors behave when serialization or deserialization fails.
///
/// Passed to [`ContextOutboundInterceptor::new`] and
/// [`ContextInboundInterceptor::new`] to configure error handling.