ruma_events/room/
thumbnail_source_serde.rs
1use ruma_common::OwnedMxcUri;
4use serde::{
5 ser::{SerializeStruct, Serializer},
6 Deserialize, Deserializer,
7};
8
9use super::{EncryptedFile, MediaSource};
10
11pub(crate) fn serialize<S>(source: &Option<MediaSource>, serializer: S) -> Result<S::Ok, S::Error>
13where
14 S: Serializer,
15{
16 if let Some(source) = source {
17 let mut st = serializer.serialize_struct("ThumbnailSource", 1)?;
18 match source {
19 MediaSource::Plain(url) => st.serialize_field("thumbnail_url", url)?,
20 MediaSource::Encrypted(file) => st.serialize_field("thumbnail_file", file)?,
21 }
22 st.end()
23 } else {
24 serializer.serialize_none()
25 }
26}
27
28pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<Option<MediaSource>, D::Error>
30where
31 D: Deserializer<'de>,
32{
33 #[derive(Deserialize)]
34 struct ThumbnailSourceJsonRepr {
35 thumbnail_url: Option<OwnedMxcUri>,
36 thumbnail_file: Option<Box<EncryptedFile>>,
37 }
38
39 match ThumbnailSourceJsonRepr::deserialize(deserializer)? {
40 ThumbnailSourceJsonRepr { thumbnail_url: None, thumbnail_file: None } => Ok(None),
41 ThumbnailSourceJsonRepr { thumbnail_file: Some(file), .. } => {
43 Ok(Some(MediaSource::Encrypted(file)))
44 }
45 ThumbnailSourceJsonRepr { thumbnail_url: Some(url), .. } => {
46 Ok(Some(MediaSource::Plain(url)))
47 }
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use assert_matches2::assert_matches;
54 use ruma_common::{mxc_uri, serde::Base64};
55 use serde::{Deserialize, Serialize};
56 use serde_json::json;
57
58 use crate::room::{EncryptedFileInit, JsonWebKeyInit, MediaSource};
59
60 #[derive(Clone, Debug, Deserialize, Serialize)]
61 struct ThumbnailSourceTest {
62 #[serde(flatten, with = "super", skip_serializing_if = "Option::is_none")]
63 source: Option<MediaSource>,
64 }
65
66 #[test]
67 fn deserialize_plain() {
68 let json = json!({ "thumbnail_url": "mxc://notareal.hs/abcdef" });
69
70 assert_matches!(
71 serde_json::from_value::<ThumbnailSourceTest>(json),
72 Ok(ThumbnailSourceTest { source: Some(MediaSource::Plain(url)) })
73 );
74 assert_eq!(url, "mxc://notareal.hs/abcdef");
75 }
76
77 #[test]
78 fn deserialize_encrypted() {
79 let json = json!({
80 "thumbnail_file": {
81 "url": "mxc://notareal.hs/abcdef",
82 "key": {
83 "kty": "oct",
84 "key_ops": ["encrypt", "decrypt"],
85 "alg": "A256CTR",
86 "k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
87 "ext": true
88 },
89 "iv": "S22dq3NAX8wAAAAAAAAAAA",
90 "hashes": {
91 "sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
92 },
93 "v": "v2",
94 },
95 });
96
97 assert_matches!(
98 serde_json::from_value::<ThumbnailSourceTest>(json),
99 Ok(ThumbnailSourceTest { source: Some(MediaSource::Encrypted(file)) })
100 );
101 assert_eq!(file.url, "mxc://notareal.hs/abcdef");
102 }
103
104 #[test]
105 fn deserialize_none_by_absence() {
106 let json = json!({});
107
108 assert_matches!(
109 serde_json::from_value::<ThumbnailSourceTest>(json).unwrap(),
110 ThumbnailSourceTest { source: None }
111 );
112 }
113
114 #[test]
115 fn deserialize_none_by_null_plain() {
116 let json = json!({ "thumbnail_url": null });
117
118 assert_matches!(
119 serde_json::from_value::<ThumbnailSourceTest>(json).unwrap(),
120 ThumbnailSourceTest { source: None }
121 );
122 }
123
124 #[test]
125 fn deserialize_none_by_null_encrypted() {
126 let json = json!({ "thumbnail_file": null });
127
128 assert_matches!(
129 serde_json::from_value::<ThumbnailSourceTest>(json).unwrap(),
130 ThumbnailSourceTest { source: None }
131 );
132 }
133
134 #[test]
135 fn serialize_plain() {
136 let request = ThumbnailSourceTest {
137 source: Some(MediaSource::Plain(mxc_uri!("mxc://notareal.hs/abcdef").into())),
138 };
139 assert_eq!(
140 serde_json::to_value(&request).unwrap(),
141 json!({ "thumbnail_url": "mxc://notareal.hs/abcdef" })
142 );
143 }
144
145 #[test]
146 fn serialize_encrypted() {
147 let request = ThumbnailSourceTest {
148 source: Some(MediaSource::Encrypted(Box::new(
149 EncryptedFileInit {
150 url: mxc_uri!("mxc://notareal.hs/abcdef").to_owned(),
151 key: JsonWebKeyInit {
152 kty: "oct".to_owned(),
153 key_ops: vec!["encrypt".to_owned(), "decrypt".to_owned()],
154 alg: "A256CTR".to_owned(),
155 k: Base64::parse("TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A").unwrap(),
156 ext: true,
157 }
158 .into(),
159 iv: Base64::parse("S22dq3NAX8wAAAAAAAAAAA").unwrap(),
160 hashes: [(
161 "sha256".to_owned(),
162 Base64::parse("aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q").unwrap(),
163 )]
164 .into(),
165 v: "v2".to_owned(),
166 }
167 .into(),
168 ))),
169 };
170 assert_eq!(
171 serde_json::to_value(&request).unwrap(),
172 json!({
173 "thumbnail_file": {
174 "url": "mxc://notareal.hs/abcdef",
175 "key": {
176 "kty": "oct",
177 "key_ops": ["encrypt", "decrypt"],
178 "alg": "A256CTR",
179 "k": "TLlG_OpX807zzQuuwv4QZGJ21_u7weemFGYJFszMn9A",
180 "ext": true
181 },
182 "iv": "S22dq3NAX8wAAAAAAAAAAA",
183 "hashes": {
184 "sha256": "aWOHudBnDkJ9IwaR1Nd8XKoI7DOrqDTwt6xDPfVGN6Q"
185 },
186 "v": "v2",
187 },
188 })
189 );
190 }
191
192 #[test]
193 fn serialize_none() {
194 let request = ThumbnailSourceTest { source: None };
195 assert_eq!(serde_json::to_value(&request).unwrap(), json!({}));
196 }
197}