diff --git a/crates/ruma-common/src/events/room.rs b/crates/ruma-common/src/events/room.rs index f4ed1494..b3b72a0a 100644 --- a/crates/ruma-common/src/events/room.rs +++ b/crates/ruma-common/src/events/room.rs @@ -5,7 +5,7 @@ use std::collections::BTreeMap; use js_int::UInt; -use serde::{Deserialize, Serialize}; +use serde::{de, Deserialize, Serialize}; use crate::{ serde::{base64::UrlSafe, Base64}, @@ -34,7 +34,7 @@ pub mod tombstone; pub mod topic; /// The source of a media file. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Serialize)] #[allow(clippy::exhaustive_enums)] pub enum MediaSource { /// The MXC URI to the unencrypted media file. @@ -46,6 +46,30 @@ pub enum MediaSource { Encrypted(Box), } +/// Custom implementation of `Deserialize`, because serde doesn't guarantee what variant will be +/// deserialized for "externally tagged"¹ enums where multiple "tag" fields exist. +/// +/// ¹ https://serde.rs/enum-representations.html +impl<'de> Deserialize<'de> for MediaSource { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + pub struct MediaSourceJsonRepr { + url: Option>, + file: Option>, + } + + match MediaSourceJsonRepr::deserialize(deserializer)? { + MediaSourceJsonRepr { url: None, file: None } => Err(de::Error::missing_field("url")), + // Prefer file if it is set + MediaSourceJsonRepr { file: Some(file), .. } => Ok(MediaSource::Encrypted(file)), + MediaSourceJsonRepr { url: Some(url), .. } => Ok(MediaSource::Plain(url)), + } + } +} + /// Metadata about an image. #[derive(Clone, Debug, Default, Deserialize, Serialize)] #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] diff --git a/crates/ruma-common/src/events/room/thumbnail_source_serde.rs b/crates/ruma-common/src/events/room/thumbnail_source_serde.rs index d57e698c..94ca45ad 100644 --- a/crates/ruma-common/src/events/room/thumbnail_source_serde.rs +++ b/crates/ruma-common/src/events/room/thumbnail_source_serde.rs @@ -1,9 +1,8 @@ //! De-/serialization functions for `Option` objects representing a thumbnail source. use serde::{ - de::Deserializer, ser::{SerializeStruct, Serializer}, - Deserialize, + Deserialize, Deserializer, }; use crate::MxcUri; @@ -32,25 +31,20 @@ pub fn deserialize<'de, D>(deserializer: D) -> Result, D::Er where D: Deserializer<'de>, { - Option::::deserialize(deserializer).map(|source| source.map(Into::into)) -} + #[derive(Deserialize)] + pub struct ThumbnailSourceJsonRepr { + thumbnail_url: Option>, + thumbnail_file: Option>, + } -#[derive(Clone, Debug, Deserialize)] -enum ThumbnailSource { - /// The MXC URI to the unencrypted media file. - #[serde(rename = "thumbnail_url")] - Plain(Box), - - /// The encryption info of the encrypted media file. - #[serde(rename = "thumbnail_file")] - Encrypted(Box), -} - -impl From for MediaSource { - fn from(source: ThumbnailSource) -> Self { - match source { - ThumbnailSource::Plain(url) => Self::Plain(url), - ThumbnailSource::Encrypted(file) => Self::Encrypted(file), + match ThumbnailSourceJsonRepr::deserialize(deserializer)? { + ThumbnailSourceJsonRepr { thumbnail_url: None, thumbnail_file: None } => Ok(None), + // Prefer file if it is set + ThumbnailSourceJsonRepr { thumbnail_file: Some(file), .. } => { + Ok(Some(MediaSource::Encrypted(file))) + } + ThumbnailSourceJsonRepr { thumbnail_url: Some(url), .. } => { + Ok(Some(MediaSource::Plain(url))) } } }