mire 0.1.1

A small, generic PostgreSQL event-sourcing library: append-only event streams, aggregates with optimistic concurrency, and subscription-based projections (requires tokio + sqlx)
Documentation
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use uuid::Uuid;

/// The serializable shape of a domain event. Every variant must report
/// a stable `event_type` string — this is what mire stores in the
/// `event_type` column and uses as the schema discriminator for the
/// event log.
///
/// **`event_type` strings are load-bearing.** Once an event has been
/// persisted under a given string, changing that string orphans the
/// historical event (it no longer deserializes into the new shape).
/// Treat them as part of your wire format.
///
/// ## Implementing via the derive macro (recommended)
///
/// Available behind the default-on `derive` feature. One line on the
/// enum sets the entity prefix; variants get CamelCase → kebab-case
/// translation automatically:
///
/// ```rust,ignore
/// use mire::EventData;
/// use serde::{Serialize, Deserialize};
///
/// #[derive(Debug, Clone, Serialize, Deserialize, EventData)]
/// #[serde(tag = "type")]
/// #[mire(entity = "account")]
/// enum AccountEvent {
///     Opened { owner: String },           // → "account.opened"
///     MoneyDeposited { amount: i64 },     // → "account.money-deposited"
///
///     #[mire(rename = "closed-v2")]       // pin a specific variant
///     Closed,                             // → "account.closed-v2"
/// }
/// ```
///
/// **Stability rule:** the `entity` attribute is fixed at the enum
/// level (no per-variant override). A variant rename in Rust *will*
/// change the persisted event_type unless you pin it with
/// `#[mire(rename = "...")]`. If you're renaming variants on a stream
/// that's already in production, pin first or you'll orphan history.
///
/// ## Implementing by hand
///
/// If you turn off the `derive` feature, hand-write the impl:
///
/// ```rust,ignore
/// impl EventData for AccountEvent {
///     fn event_type(&self) -> &'static str {
///         match self {
///             AccountEvent::Opened { .. } => "account.opened",
///             // ...
///         }
///     }
/// }
/// ```
pub trait EventData: Serialize + DeserializeOwned + Send + Sync + 'static {
    fn event_type(&self) -> &'static str;
}

#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EventMetadata {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub correlation_id: Option<Uuid>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub causation_id: Option<Uuid>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub actor: Option<String>,
}

#[derive(Debug, Clone)]
pub struct Event<E: EventData> {
    pub event: E,
    pub metadata: serde_json::Value,
}

impl<E: EventData> Event<E> {
    pub fn new(event: E) -> Self {
        Self {
            event,
            metadata: serde_json::json!({}),
        }
    }

    pub fn with_metadata(mut self, metadata: serde_json::Value) -> Self {
        self.metadata = metadata;
        self
    }
}

#[derive(Debug, Clone)]
pub struct RecordedEvent {
    pub global_position: i64,
    pub stream_id: String,
    pub stream_version: i64,
    pub event_type: String,
    pub data: serde_json::Value,
    pub metadata: serde_json::Value,
    /// The Postgres transaction (xid8) that appended this event. Used together
    /// with `global_position` as the subscription cursor; see the schema notes.
    pub transaction_id: u64,
    pub created_at: DateTime<Utc>,
}