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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
//! Transport abstraction.
//!
//! The SDK is deliberately transport-agnostic. Any framed byte stream —
//! WebSocket, raw TCP, Unix socket, stdio, an in-memory pair for tests —
//! can back a [`Transport`] implementation. The client consumes typed
//! [`TransportMessage`]s; framing and TLS are the transport's concern.
//!
//! # Implementing a transport
//!
//! Three async methods are required: [`Transport::send`],
//! [`Transport::recv`], and (optionally) [`Transport::close`]. The
//! crate uses the `impl Future` form of async-fn-in-trait, so no
//! dynamic dispatch box is required.
//!
//! ```
//! use ahp::{Transport, TransportError, TransportMessage};
//! use std::future::Future;
//! use tokio::sync::mpsc;
//!
//! /// One half of an in-memory transport pair — handy for tests.
//! pub struct MemTransport {
//! tx: mpsc::Sender<TransportMessage>,
//! rx: mpsc::Receiver<TransportMessage>,
//! }
//!
//! impl Transport for MemTransport {
//! async fn send(&mut self, msg: TransportMessage) -> Result<(), TransportError> {
//! self.tx.send(msg).await.map_err(|_| TransportError::Closed)
//! }
//! async fn recv(&mut self) -> Result<Option<TransportMessage>, TransportError> {
//! Ok(self.rx.recv().await)
//! }
//! }
//! ```
//!
//! For ready-made transports, see the [`ahp-ws`](https://docs.rs/ahp-ws)
//! crate (WebSocket via `tokio-tungstenite`).
//!
//! # Type-erased transports
//!
//! [`Transport`] is intentionally generic and not object-safe — the
//! `impl Future` returns let it stay zero-cost on the hot path. When you
//! need to store transports of different concrete types behind one
//! handle (a registry of hosts, a transport factory that picks WebSocket
//! vs stdio at runtime, etc.), wrap each one in [`BoxedTransport`]:
//!
//! ```no_run
//! # use ahp::transport::BoxedTransport;
//! # use ahp::TransportError;
//! # async fn open_a() -> Result<BoxedTransport, TransportError> { unimplemented!() }
//! # async fn open_b() -> Result<BoxedTransport, TransportError> { unimplemented!() }
//! # async fn run() -> Result<(), TransportError> {
//! let transports: Vec<BoxedTransport> = vec![
//! open_a().await?,
//! open_b().await?,
//! ];
//! # let _ = transports;
//! # Ok(()) }
//! ```
//!
//! `BoxedTransport` itself implements [`Transport`], so it can be passed
//! straight to [`crate::Client::connect`].
use Future;
use Pin;
use crateTransportError;
use JsonRpcMessage;
/// A single message flowing in or out over a [`Transport`].
///
/// This is a thin wrapper around [`JsonRpcMessage`] so transports can
/// avoid re-serializing when they already have a decoded value, while
/// remaining free to hand us raw bytes if that's more natural.
/// Pluggable transport trait. Implementations are driven by the
/// [`crate::Client`]; they must deliver inbound messages in order and
/// accept outbound sends serially.
///
/// Transports are expected to be full-duplex and half-closable — the
/// client sends indefinitely until the underlying connection closes,
/// and `recv` signals closure by returning `None`.
// ─── Object-safe adapter ────────────────────────────────────────────────────
/// Object-safe sibling of [`Transport`].
///
/// This trait is what [`BoxedTransport`] stores internally. It is
/// implemented automatically for every type implementing [`Transport`],
/// so users never need to implement it directly — wrap any
/// [`Transport`] in [`BoxedTransport::new`] to type-erase it.
///
/// The futures returned here are heap-allocated, so [`BoxedTransport`]
/// is slightly more expensive than using a concrete [`Transport`].
/// Reach for it when heterogeneous storage is more important than the
/// allocation cost (typically: registries that hold one transport per
/// host).
/// Type-erased [`Transport`].
///
/// Wraps any concrete [`Transport`] implementation in a `Box<dyn ..>`
/// while still satisfying [`Transport`] itself, so the result can be
/// passed to [`crate::Client::connect`]. Use this when a registry,
/// factory, or container needs to hold transports of different concrete
/// types behind a single handle (for example, the multi-host runtime
/// in [`crate::hosts`]).
///
/// ```
/// # async fn run() -> Result<(), ahp::TransportError> {
/// use ahp::transport::{BoxedTransport, TransportMessage};
/// use ahp::{Transport, TransportError};
/// use tokio::sync::mpsc;
///
/// struct NoopTransport;
/// impl Transport for NoopTransport {
/// async fn send(&mut self, _: TransportMessage) -> Result<(), TransportError> { Ok(()) }
/// async fn recv(&mut self) -> Result<Option<TransportMessage>, TransportError> { Ok(None) }
/// }
///
/// let boxed: BoxedTransport = BoxedTransport::new(NoopTransport);
/// // `boxed` itself implements `Transport` and can be handed to `Client::connect`.
/// # let _ = boxed;
/// # Ok(()) }
/// ```