Skip to main content

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}