logo
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
use js_int::Int;
use serde::{Deserialize, Serialize};
use serde_json::{from_str as from_json_str, value::RawValue as RawJsonValue};

#[cfg(feature = "unstable-msc2675")]
use super::relation::Relations;
use super::{room::redaction::SyncRoomRedactionEvent, StateEventContent};
use crate::{serde::Raw, OwnedTransactionId};

/// Extra information about a message event that is not incorporated into the event's hash.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct MessageLikeUnsigned {
    /// The time in milliseconds that has elapsed since the event was sent.
    ///
    /// This field is generated by the local homeserver, and may be incorrect if the local time on
    /// at least one of the two servers is out of sync, which can cause the age to either be
    /// negative or greater than it actually is.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub age: Option<Int>,

    /// The client-supplied transaction ID, if the client being given the event is the same one
    /// which sent it.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction_id: Option<OwnedTransactionId>,

    /// Server-compiled information from other events relating to this event.
    #[cfg(feature = "unstable-msc2675")]
    #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")]
    pub relations: Option<Relations>,
}

impl MessageLikeUnsigned {
    /// Create a new `Unsigned` with fields set to `None`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Whether this unsigned data is empty (all fields are `None`).
    ///
    /// This method is used to determine whether to skip serializing the `unsigned` field in room
    /// events. Do not use it to determine whether an incoming `unsigned` field was present - it
    /// could still have been present but contained none of the known fields.
    pub fn is_empty(&self) -> bool {
        #[cfg(not(feature = "unstable-msc2675"))]
        {
            self.age.is_none() && self.transaction_id.is_none()
        }

        #[cfg(feature = "unstable-msc2675")]
        {
            self.age.is_none() && self.transaction_id.is_none() && self.relations.is_none()
        }
    }
}

/// Extra information about a state event that is not incorporated into the event's hash.
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct StateUnsigned<C: StateEventContent> {
    /// The time in milliseconds that has elapsed since the event was sent.
    ///
    /// This field is generated by the local homeserver, and may be incorrect if the local time on
    /// at least one of the two servers is out of sync, which can cause the age to either be
    /// negative or greater than it actually is.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub age: Option<Int>,

    /// The client-supplied transaction ID, if the client being given the event is the same one
    /// which sent it.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub transaction_id: Option<OwnedTransactionId>,

    /// Optional previous content of the event.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub prev_content: Option<C>,

    /// Server-compiled information from other events relating to this event.
    #[cfg(feature = "unstable-msc2675")]
    #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")]
    pub relations: Option<Relations>,
}

impl<C: StateEventContent> StateUnsigned<C> {
    /// Create a new `Unsigned` with fields set to `None`.
    pub fn new() -> Self {
        Self {
            age: None,
            transaction_id: None,
            prev_content: None,
            #[cfg(feature = "unstable-msc2675")]
            relations: None,
        }
    }

    /// Whether this unsigned data is empty (all fields are `None`).
    ///
    /// This method is used to determine whether to skip serializing the `unsigned` field in room
    /// events. Do not use it to determine whether an incoming `unsigned` field was present - it
    /// could still have been present but contained none of the known fields.
    pub fn is_empty(&self) -> bool {
        #[cfg(not(feature = "unstable-msc2675"))]
        {
            self.age.is_none() && self.transaction_id.is_none() && self.prev_content.is_none()
        }

        #[cfg(feature = "unstable-msc2675")]
        {
            self.age.is_none()
                && self.transaction_id.is_none()
                && self.prev_content.is_none()
                && self.relations.is_none()
        }
    }
}

/// Helper functions for proc-macro code.
///
/// Needs to be public for UI tests.
#[doc(hidden)]
impl<C: StateEventContent> StateUnsigned<C> {
    pub fn _from_parts(event_type: &str, object: &RawJsonValue) -> serde_json::Result<Self> {
        #[derive(Deserialize)]
        #[serde(bound = "")] // Disable default C: Deserialize bound
        struct WithRawPrevContent<C> {
            #[serde(skip_serializing_if = "Option::is_none")]
            age: Option<Int>,
            #[serde(skip_serializing_if = "Option::is_none")]
            transaction_id: Option<OwnedTransactionId>,
            prev_content: Option<Raw<C>>,
            #[cfg(feature = "unstable-msc2675")]
            #[serde(rename = "m.relations", skip_serializing_if = "Option::is_none")]
            relations: Option<Relations>,
        }

        let raw: WithRawPrevContent<C> = from_json_str(object.get())?;
        let prev_content =
            raw.prev_content.map(|r| r.deserialize_content(event_type.into())).transpose()?;

        Ok(Self {
            age: raw.age,
            transaction_id: raw.transaction_id,
            #[cfg(feature = "unstable-msc2675")]
            relations: raw.relations,
            prev_content,
        })
    }

    pub fn _map_prev_content<T>(&self, f: impl FnOnce(&C) -> T) -> StateUnsigned<T>
    where
        T: StateEventContent,
    {
        StateUnsigned {
            age: self.age,
            transaction_id: self.transaction_id.clone(),
            prev_content: self.prev_content.as_ref().map(f),
            #[cfg(feature = "unstable-msc2675")]
            relations: self.relations.clone(),
        }
    }
}

impl<C: StateEventContent> Default for StateUnsigned<C> {
    fn default() -> Self {
        Self::new()
    }
}

/// Extra information about a redacted event that is not incorporated into the event's hash.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct RedactedUnsigned {
    /// The event that redacted this event, if any.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub redacted_because: Option<Box<SyncRoomRedactionEvent>>,
}

impl RedactedUnsigned {
    /// Create a new `RedactedUnsigned` with field set to `None`.
    pub fn new() -> Self {
        Self::default()
    }

    /// Create a new `RedactedUnsigned` with the given redacted because.
    pub fn new_because(redacted_because: Box<SyncRoomRedactionEvent>) -> Self {
        Self { redacted_because: Some(redacted_because) }
    }

    /// Whether this unsigned data is empty (`redacted_because` is `None`).
    ///
    /// This method is used to determine whether to skip serializing the `unsigned` field in
    /// redacted room events. Do not use it to determine whether an incoming `unsigned` field
    /// was present - it could still have been present but contained none of the known fields.
    pub fn is_empty(&self) -> bool {
        self.redacted_because.is_none()
    }
}