reliar_core/message.rs
1//! Stable message contract identity (SRS §10, §10.1, ADR 0010).
2
3use core::fmt;
4use std::borrow::Cow;
5
6/// A type that can be built into an [`Envelope`](crate::Envelope) and persisted or published.
7///
8/// `TYPE`/`VERSION` are stable **application contracts**: they identify a message across
9/// serialization, storage and the wire, and are never derived from
10/// `std::any::type_name::<T>()` or a module path — renaming or moving the Rust type SHALL NOT
11/// orphan a pending row or a message already in flight (ADR 0010).
12pub trait Message: serde::Serialize + serde::de::DeserializeOwned {
13 /// The message's name, e.g. `"orders.created"`. Stable once anything has published it.
14 const TYPE: &'static str;
15 /// The message's version. Bump when the wire shape changes incompatibly.
16 const VERSION: u16;
17}
18
19/// A message's name and version, carried separately so a query can filter a name across every
20/// version (§24). Renders as `"{name}.v{version}"` via its [`Display`](fmt::Display) impl.
21#[derive(Clone, Debug, PartialEq, Eq, Hash)]
22pub struct MessageType {
23 name: Cow<'static, str>,
24 version: u16,
25}
26
27impl MessageType {
28 /// Builds a `MessageType` from a `'static` name and a version.
29 #[must_use]
30 pub const fn new(name: &'static str, version: u16) -> Self {
31 Self {
32 name: Cow::Borrowed(name),
33 version,
34 }
35 }
36
37 /// Rehydration path: a provider reads `message_type`/`message_version` columns back into a
38 /// `MessageType` for which it has no Rust type.
39 pub fn from_parts(name: impl Into<Cow<'static, str>>, version: u16) -> Self {
40 Self {
41 name: name.into(),
42 version,
43 }
44 }
45
46 /// Builds the `MessageType` a `T: Message` declares: `T::TYPE` + `T::VERSION`. Never derived
47 /// from `std::any::type_name::<T>()`.
48 #[must_use]
49 pub fn of<T: Message>() -> Self {
50 Self::new(T::TYPE, T::VERSION)
51 }
52
53 /// The message name, e.g. `"orders.created"`.
54 #[must_use]
55 pub fn name(&self) -> &str {
56 &self.name
57 }
58
59 /// The message version.
60 #[must_use]
61 pub const fn version(&self) -> u16 {
62 self.version
63 }
64}
65
66/// Renders `"{name}.v{version}"`, e.g. `orders.created.v1`. **A stable public contract**:
67/// clients parse this string. Two distinct Rust types sharing `TYPE`/`VERSION` render
68/// identically — that is intended, not a bug (ADR 0010, §43.A.3).
69impl fmt::Display for MessageType {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 write!(f, "{}.v{}", self.name, self.version)
72 }
73}