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
//! The imports a routes file on Pub/Sub writes every time, in one glob.
//!
//! `use ruststream_gcp_pubsub::prelude::*;` carries the framework's own prelude, this crate's
//! broker and subscription descriptor, its publish policy under the mount-site name [`Publish`],
//! the live publisher, and the [`PubSubOrdering`] step with the
//! [`PubSubPublishOptions`] it writes into.
//!
//! The two sides of a service import different things, and that is what keeps the two
//! vocabularies apart. A file of handler bodies imports the framework's prelude alone and bounds
//! a slot with a capability trait ([`Publisher`]), so it names no broker at all. A routes file
//! imports this glob and attaches policies under their uniform mount-site names, so moving a
//! service to another broker changes the one import rather than every mount site. The prefixed
//! originals stay at the crate root for a file that speaks to two brokers at once and has to say
//! which one it means.
//!
//! A body that adjusts a per-message setting is the one exception: it names the
//! [`ordering_key`](PubSubOrdering::ordering_key) step, so it imports this glob too and bounds its
//! slot `Out<impl Publisher<Options = PubSubPublishOptions>, Marker>`. Its signature then says
//! which broker the handler is tied to.
//!
//! # Examples
//!
//! A routes file: the broker, the descriptor and the policy all come from the one glob.
//!
//! ```
//! # mod demo {
//! use ruststream_gcp_pubsub::prelude::*;
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Debug, Deserialize, Serialize, Outgoing)]
//! struct Order {
//! id: u64,
//! }
//!
//! /// The body names no broker: its slot is bounded by the framework's capability alone.
//! #[subscriber(GooglePubSub::new("orders-workers"))]
//! async fn confirm(order: &Order, Out(out): Out<impl Publisher>) -> HandlerOutcome {
//! match out.message(order).to("confirmations").publish().await {
//! Ok(_) => HandlerOutcome::ack(),
//! Err(_) => HandlerOutcome::retry(),
//! }
//! }
//!
//! #[ruststream::app]
//! fn app() -> impl App {
//! RustStream::new(AppInfo::new("orders", "0.1.0"))
//! .with_broker(PubSubBroker::new("my-project"), |b| {
//! b.include(confirm).out(DefaultSlot, Publish::default()).build();
//! })
//! }
//! # }
//! # fn main() {}
//! ```
pub use cratePubSubPublish as Publish;
pub use crate::;
pub use *;
// `Publish` is a policy name, and the framework's prelude exports nothing under it, so this
// re-export names one thing and collides with nothing: a mount site reads the same whichever
// broker it is on, and swapping brokers swaps the glob. The capability vocabulary a handler body
// bounds its slot with comes from the framework's prelude, which such a file imports on its own.
// `tests/prelude_pubsub.rs` pins both halves.
//
// Never alias a policy to a framework trait name (`Publisher`, `TransactionalPublisher`,
// `OwnedTransactions`, `RequestReply`): a body that globs both preludes has to keep resolving
// those to the framework's traits.
// `Partitioned` stays out on purpose: the core surfaces `partition_key` as a defaulted method on
// `IncomingMessage`, which this glob already carries, and this crate's deliveries override it, so
// a glob holding both traits makes `message.partition_key()` ambiguous (E0034) on a concrete
// delivery. A generic `&impl Partitioned` bound still compiles, which is how the trap hides.