ruma-common 0.9.2

Common types for other ruma crates.
Documentation
use std::fmt;

use serde::{de::DeserializeOwned, Serialize};
use serde_json::value::RawValue as RawJsonValue;

use crate::serde::Raw;

use super::{
    EphemeralRoomEventType, GlobalAccountDataEventType, MessageLikeEventType,
    RoomAccountDataEventType, StateEventType, ToDeviceEventType,
};

/// The base trait that all event content types implement.
///
/// Use [`macros::EventContent`] to derive this traits. It is not meant to be implemented manually.
///
/// [`macros::EventContent`]: super::macros::EventContent
pub trait EventContent: Sized + Serialize {
    /// The Rust enum for the event kind's known types.
    type EventType;

    /// Get the event's type, like `m.room.message`.
    fn event_type(&self) -> Self::EventType;

    /// Constructs the given event content.
    #[doc(hidden)]
    fn from_parts(event_type: &str, content: &RawJsonValue) -> serde_json::Result<Self>;
}

impl<T> Raw<T>
where
    T: EventContent,
    T::EventType: fmt::Display,
{
    /// Try to deserialize the JSON as an event's content.
    pub fn deserialize_content(&self, event_type: T::EventType) -> serde_json::Result<T> {
        T::from_parts(&event_type.to_string(), self.json())
    }
}

/// The base trait that all redacted event content types implement.
///
/// This trait's associated functions and methods should not be used to build
/// redacted events, prefer the `redact` method on `AnyStateEvent` and
/// `AnyMessageLikeEvent` and their "sync" and "stripped" counterparts.
/// The `RedactedEventContent` trait is an implementation detail, ruma makes no
/// API guarantees.
pub trait RedactedEventContent: EventContent {
    /// Constructs the redacted event content.
    ///
    /// If called for anything but "empty" redacted content this will error.
    #[doc(hidden)]
    fn empty(_event_type: &str) -> serde_json::Result<Self> {
        Err(serde::de::Error::custom("this event is not redacted"))
    }

    /// Determines if the redacted event content needs to serialize fields.
    #[doc(hidden)]
    fn has_serialize_fields(&self) -> bool;

    /// Determines if the redacted event content needs to deserialize fields.
    #[doc(hidden)]
    fn has_deserialize_fields() -> HasDeserializeFields;
}

/// `HasDeserializeFields` is used in the code generated by the `Event` derive
/// to aid in deserializing redacted events.
#[doc(hidden)]
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum HasDeserializeFields {
    /// Deserialize the event's content, failing if invalid.
    True,

    /// Return the redacted version of this event's content.
    False,

    /// `Optional` is used for `RedactedAliasesEventContent` since it has
    /// an empty version and one with content left after redaction that
    /// must be supported together.
    Optional,
}

/// Trait for abstracting over event content structs.
///
/// … but *not* enums which don't always have an event type and kind (e.g. message vs state) that's
/// fixed / known at compile time.
pub trait StaticEventContent: EventContent {
    /// The event's "kind".
    ///
    /// See the type's documentation.
    const KIND: EventKind;

    /// The event type.
    const TYPE: &'static str;
}

/// The "kind" of an event.
///
/// This corresponds directly to the event content marker traits.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[non_exhaustive]
pub enum EventKind {
    /// Global account data event kind.
    GlobalAccountData,

    /// Room account data event kind.
    RoomAccountData,

    /// Ephemeral room event kind.
    EphemeralRoomData,

    /// Message-like event kind.
    ///
    /// Since redacted / non-redacted message-like events are used in the same places but have
    /// different sets of fields, these two variations are treated as two closely-related event
    /// kinds.
    MessageLike {
        /// Redacted variation?
        redacted: bool,
    },

    /// State event kind.
    ///
    /// Since redacted / non-redacted state events are used in the same places but have different
    /// sets of fields, these two variations are treated as two closely-related event kinds.
    State {
        /// Redacted variation?
        redacted: bool,
    },

    /// To-device event kind.
    ToDevice,

    /// Presence event kind.
    Presence,
}

macro_rules! trait_aliases {
    // need to use `,` instead of `+` because (1) path can't be followed by `+`
    // and (2) `+` can't be used as a separator since it's a repetition operator
    ($(
        $( #[doc = $docs:literal] )*
        trait $id:ident = $( $def:path ),+;
    )*) => {
        $(
            $( #[doc = $docs] )*
            pub trait $id: $($def+)+ {}
            impl<T: $($def+)+> $id for T {}
        )*
    }
}

trait_aliases! {
    /// An alias for `EventContent<EventType = GlobalAccountDataEventType>`.
    trait GlobalAccountDataEventContent = EventContent<EventType = GlobalAccountDataEventType>;

    /// An alias for `EventContent<EventType = RoomAccountDataEventType>`.
    trait RoomAccountDataEventContent = EventContent<EventType = RoomAccountDataEventType>;

    /// An alias for `EventContent<EventType = EphemeralRoomEventType>`.
    trait EphemeralRoomEventContent = EventContent<EventType = EphemeralRoomEventType>;

    /// An alias for `EventContent<EventType = MessageLikeEventType>`.
    trait MessageLikeEventContent = EventContent<EventType = MessageLikeEventType>;

    /// An alias for `MessageLikeEventContent + RedactedEventContent`.
    trait RedactedMessageLikeEventContent = MessageLikeEventContent, RedactedEventContent;

    /// An alias for `StateEventContent + RedactedEventContent`.
    trait RedactedStateEventContent = StateEventContent, RedactedEventContent;

    /// An alias for `EventContent<EventType = ToDeviceEventType>`.
    trait ToDeviceEventContent = EventContent<EventType = ToDeviceEventType>;
}

/// An alias for `EventContent<EventType = StateEventType>`.
pub trait StateEventContent: EventContent<EventType = StateEventType> {
    /// The type of the event's `state_key` field.
    type StateKey: AsRef<str> + Clone + fmt::Debug + DeserializeOwned + Serialize;
}