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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
//! The inbox: task queues in tables a service describes with its own structs.
pub
pub
pub
pub
pub
pub
pub
use Debug;
use ;
use TableSpec;
pub use ;
pub use ;
pub use ;
pub use AnyDialect;
pub use ;
pub use InboxDelivery;
pub use SqlxBrokerError;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use ;
pub use InboxQueue;
pub use ;
pub use InboxSubscriber;
pub use ;
pub use ;
pub use ;
/// A struct that describes a queue table; `#[derive(Inbox)]` implements it.
///
/// The struct puts its table in one of two modes. In payload mode a `#[field(payload)]` field
/// holds the message, and a codec decodes it for the handler ([`PayloadRow`]). In row mode the
/// struct has no such field, and the handler takes the struct itself, `&Row` or `&[Row]` (see
/// [Row mode](crate#row-mode)).
///
/// # Examples
///
/// ```
/// # #[cfg(feature = "postgres")]
/// # mod demo {
/// use ruststream_sqlx::prelude::*;
/// use serde::Deserialize;
/// use sqlx::PgPool;
///
/// // The derive implements `InboxRow` for `SendEmail`: the table `app.email_jobs`, its id and
/// // its payload column.
/// #[derive(Inbox, sqlx::FromRow)]
/// #[inbox(table = "email_jobs", schema = "app")]
/// pub struct SendEmail {
/// #[field(id)]
/// job_id: i64,
/// #[field(payload)]
/// payload: Vec<u8>,
/// }
///
/// #[derive(Deserialize)]
/// struct Email {
/// to: String,
/// }
///
/// #[subscriber(InboxQueue::<SendEmail>::new("emails"))]
/// async fn send(email: &Email) -> HandlerOutcome {
/// tracing::info!(to = %email.to, "sending");
/// HandlerOutcome::ack()
/// }
///
/// pub fn app(pool: PgPool) -> RustStream {
/// RustStream::new(AppInfo::new("mailer", "0.1.0")).with_broker(SqlxBroker::new(pool), |b| {
/// b.include(send);
/// })
/// }
/// # }
/// # fn main() {}
/// ```
/// A row a subscription delivers, the type of its id, and how its message reaches a handler.
/// Machinery: `#[derive(Inbox)]` implements it beside [`InboxRow`], and the row of a by-name
/// subscription implements it without a table description of its own.
/// How a delivery hands a row's message to its handler. Machinery; the crate's two lanes are the
/// only ones.
/// Payload mode: the handler takes the payload decoded by a codec.
/// Row mode: the handler takes the row itself, `&Row`, or a batch of them, `&[Row]`.
// No `Clone` here: the carried lane asks it of the row where the derive writes `Input`, and one
// check gives one error.
/// A queue row that carries its message as bytes: payload mode.
///
/// The derive implements it for a struct with a `#[field(payload)]` field; a table described by
/// hand implements it where its description sets [`InboxSpec::payload`]. A subscription hands the
/// handler the payload decoded by a codec or a `Deserialized` type, as on any broker, and the
/// bytes are lent from the row without a copy. Routes and by-name subscriptions read tables in
/// this mode.
///
/// A struct without a payload field is in row mode: its handler takes the row itself, and the
/// derive's [`insert`](crate::Insert::insert) or a [`Repository`] over the service's own
/// [`Publish`] writes its tasks (see [Row mode](crate#row-mode)).
///
/// # Examples
///
/// ```
/// # #[cfg(feature = "postgres")]
/// # mod demo {
/// use ruststream::OutgoingMessage;
/// use ruststream_sqlx::prelude::*;
/// use serde::Deserialize;
/// use sqlx::{PgConnection, PgPool, Postgres};
///
/// // `body` holds the message: `Job` is in payload mode, so a route can lead a name to it.
/// #[derive(Inbox, sqlx::FromRow)]
/// #[inbox(table = "jobs")]
/// pub struct Job {
/// #[field(id, generated)]
/// id: i64,
/// #[field(group)]
/// name: String,
/// #[field(payload)]
/// body: Vec<u8>,
/// }
///
/// impl Publish<Postgres> for Job {
/// async fn publish(
/// conn: &mut PgConnection,
/// message: &OutgoingMessage<'_>,
/// ) -> Result<(), sqlx::Error> {
/// sqlx::query("INSERT INTO jobs (name, body) VALUES ($1, $2)")
/// .bind(message.name())
/// .bind(message.payload())
/// .execute(conn)
/// .await?;
/// Ok(())
/// }
/// }
///
/// #[derive(Deserialize)]
/// struct Email {
/// to: String,
/// }
///
/// // The codec decodes `Email` from the bytes lent from `body`.
/// #[subscriber("emails")]
/// async fn send(email: &Email) -> HandlerOutcome {
/// tracing::info!(to = %email.to, "sending");
/// HandlerOutcome::ack()
/// }
///
/// pub fn app(pool: PgPool) -> RustStream {
/// RustStream::new(AppInfo::new("mailer", "0.1.0")).with_broker(
/// SqlxBroker::new(pool).route::<Job>("emails"),
/// |b| {
/// b.include(send);
/// },
/// )
/// }
/// # }
/// # fn main() {}
/// ```
///
/// A table described by hand names the payload column's type, which a by-name subscription
/// reads it in:
///
/// ```
/// # #[cfg(all(feature = "sqlite", feature = "chrono"))]
/// # mod demo {
/// use chrono::{DateTime, Utc};
/// use ruststream_sqlx::dialect::Column;
/// use ruststream_sqlx::prelude::*;
/// use ruststream_sqlx::spec::{Lease, Payload};
/// use ruststream_sqlx::{InboxSpec, InboxTable, PayloadRow};
/// use serde::Deserialize;
/// use sqlx::{Sqlite, SqlitePool};
///
/// #[derive(sqlx::FromRow)]
/// pub struct Job {
/// id: i64,
/// body: Vec<u8>,
/// }
///
/// impl InboxTable for Job {
/// type Id = i64;
/// type Table = InboxSpec<(Lease<DateTime<Utc>>, Payload)>;
/// const TABLE: Self::Table = InboxSpec::new("jobs", Column::new("id").generated())
/// .lease(Column::new("locked_until"))
/// .group(Column::new("name"))
/// .payload(Column::new("body"));
///
/// fn id(&self) -> &i64 {
/// &self.id
/// }
/// }
///
/// impl PayloadRow for Job {
/// type Column = Vec<u8>;
///
/// fn payload(&self) -> &[u8] {
/// &self.body
/// }
/// }
///
/// #[derive(Deserialize)]
/// struct Email {
/// to: String,
/// }
///
/// // The codec decodes `Email` from the bytes lent from `body`.
/// #[subscriber(InboxQueue::<Job>::new("emails"))]
/// async fn send(email: &Email) -> HandlerOutcome {
/// tracing::info!(to = %email.to, "sending");
/// HandlerOutcome::ack()
/// }
///
/// pub fn app(pool: SqlitePool) -> RustStream {
/// RustStream::new(AppInfo::new("mailer", "0.1.0")).with_broker(
/// SqlxBroker::<Sqlite>::new(pool),
/// |b| {
/// b.include(send);
/// },
/// )
/// }
/// # }
/// # fn main() {}
/// ```