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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use std::borrow::Cow;

use ruma_common::serde::JsonObject;

use crate::relation::{CustomRelation, InReplyTo, RelationType, Replacement, Thread};

/// Message event relationship.
#[derive(Clone, Debug)]
#[allow(clippy::manual_non_exhaustive)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum Relation<C> {
    /// An `m.in_reply_to` relation indicating that the event is a reply to another event.
    Reply {
        /// Information about another message being replied to.
        in_reply_to: InReplyTo,
    },

    /// An event that replaces another event.
    Replacement(Replacement<C>),

    /// An event that belongs to a thread.
    Thread(Thread),

    #[doc(hidden)]
    _Custom(CustomRelation),
}

impl<C> Relation<C> {
    /// The type of this `Relation`.
    ///
    /// Returns an `Option` because the `Reply` relation does not have a`rel_type` field.
    pub fn rel_type(&self) -> Option<RelationType> {
        match self {
            Relation::Reply { .. } => None,
            Relation::Replacement(_) => Some(RelationType::Replacement),
            Relation::Thread(_) => Some(RelationType::Thread),
            Relation::_Custom(c) => c.rel_type(),
        }
    }

    /// The associated data.
    ///
    /// The returned JSON object holds the contents of `m.relates_to`, including `rel_type` and
    /// `event_id` if present, but not things like `m.new_content` for `m.replace` relations that
    /// live next to `m.relates_to`.
    ///
    /// Prefer to use the public variants of `Relation` where possible; this method is meant to
    /// be used for custom relations only.
    pub fn data(&self) -> Cow<'_, JsonObject>
    where
        C: Clone,
    {
        if let Relation::_Custom(CustomRelation(data)) = self {
            Cow::Borrowed(data)
        } else {
            Cow::Owned(self.serialize_data())
        }
    }
}

/// Message event relationship, except a replacement.
#[derive(Clone, Debug)]
#[allow(clippy::manual_non_exhaustive)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum RelationWithoutReplacement {
    /// An `m.in_reply_to` relation indicating that the event is a reply to another event.
    Reply {
        /// Information about another message being replied to.
        in_reply_to: InReplyTo,
    },

    /// An event that belongs to a thread.
    Thread(Thread),

    #[doc(hidden)]
    _Custom(CustomRelation),
}

impl RelationWithoutReplacement {
    /// The type of this `Relation`.
    ///
    /// Returns an `Option` because the `Reply` relation does not have a`rel_type` field.
    pub fn rel_type(&self) -> Option<RelationType> {
        match self {
            Self::Reply { .. } => None,
            Self::Thread(_) => Some(RelationType::Thread),
            Self::_Custom(c) => c.rel_type(),
        }
    }

    /// The associated data.
    ///
    /// The returned JSON object holds the contents of `m.relates_to`, including `rel_type` and
    /// `event_id` if present, but not things like `m.new_content` for `m.replace` relations that
    /// live next to `m.relates_to`.
    ///
    /// Prefer to use the public variants of `Relation` where possible; this method is meant to
    /// be used for custom relations only.
    pub fn data(&self) -> Cow<'_, JsonObject> {
        if let Self::_Custom(CustomRelation(data)) = self {
            Cow::Borrowed(data)
        } else {
            Cow::Owned(self.serialize_data())
        }
    }
}

impl<C> TryFrom<Relation<C>> for RelationWithoutReplacement {
    type Error = Replacement<C>;

    fn try_from(value: Relation<C>) -> Result<Self, Self::Error> {
        let rel = match value {
            Relation::Reply { in_reply_to } => Self::Reply { in_reply_to },
            Relation::Replacement(r) => return Err(r),
            Relation::Thread(t) => Self::Thread(t),
            Relation::_Custom(c) => Self::_Custom(c),
        };

        Ok(rel)
    }
}