events: Rework Relation serde
Relation types now implement `Deserialize`, `Serialize`.
This commit is contained in:
		
							parent
							
								
									06f8b81ace
								
							
						
					
					
						commit
						a81880c68f
					
				| @ -3,6 +3,12 @@ | |||||||
| Improvements: | Improvements: | ||||||
| 
 | 
 | ||||||
| * Add `From` implementations for event and event content enums | * Add `From` implementations for event and event content enums | ||||||
|  | * It's now an error for a `room::message::Relation` to be `Replaces` without | ||||||
|  |   there being `new_content` | ||||||
|  |   * Previously, this used to set the relation to `None` | ||||||
|  | * Unsupported relations are now deserialized to `relates_to: Some(_)` instead of | ||||||
|  |   `None` | ||||||
|  |   * It's not possible to inspect the inner value though | ||||||
| 
 | 
 | ||||||
| # 0.24.4 | # 0.24.4 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -33,7 +33,7 @@ pub struct EncryptedEventContent { | |||||||
|     /// Information about related messages for [rich replies].
 |     /// Information about related messages for [rich replies].
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// [rich replies]: https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies
 |     /// [rich replies]: https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies
 | ||||||
|     #[serde(flatten, with = "relation_serde", skip_serializing_if = "Option::is_none")] |     #[serde(flatten, skip_serializing_if = "Option::is_none")] | ||||||
|     pub relates_to: Option<Relation>, |     pub relates_to: Option<Relation>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -91,6 +91,7 @@ pub enum EncryptedEventScheme { | |||||||
| ///
 | ///
 | ||||||
| /// Outside of the encrypted payload to support server aggregation.
 | /// Outside of the encrypted payload to support server aggregation.
 | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
|  | #[allow(clippy::manual_non_exhaustive)] | ||||||
| #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] | #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] | ||||||
| pub enum Relation { | pub enum Relation { | ||||||
|     /// An `m.in_reply_to` relation indicating that the event is a reply to another event.
 |     /// An `m.in_reply_to` relation indicating that the event is a reply to another event.
 | ||||||
| @ -113,6 +114,9 @@ pub enum Relation { | |||||||
|     #[cfg(feature = "unstable-pre-spec")] |     #[cfg(feature = "unstable-pre-spec")] | ||||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))] |     #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))] | ||||||
|     Annotation(Annotation), |     Annotation(Annotation), | ||||||
|  | 
 | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     _Custom, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// The event this relation belongs to replaces another event.
 | /// The event this relation belongs to replaces another event.
 | ||||||
| @ -263,6 +267,7 @@ impl From<message::Relation> for Relation { | |||||||
|             message::Relation::Replacement(re) => { |             message::Relation::Replacement(re) => { | ||||||
|                 Self::Replacement(Replacement { event_id: re.event_id }) |                 Self::Replacement(Replacement { event_id: re.event_id }) | ||||||
|             } |             } | ||||||
|  |             message::Relation::_Custom => Self::_Custom, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,77 +1,69 @@ | |||||||
| use serde::{ser::SerializeStruct as _, Deserialize, Deserializer, Serialize, Serializer}; | use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "unstable-pre-spec")] | #[cfg(feature = "unstable-pre-spec")] | ||||||
| use super::{Annotation, Reference, Replacement}; | use super::{Annotation, Reference, Replacement}; | ||||||
| use super::{InReplyTo, Relation}; | use super::{InReplyTo, Relation}; | ||||||
| 
 | 
 | ||||||
| pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Relation>, D::Error> | impl<'de> Deserialize<'de> for Relation { | ||||||
|  |     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||||
|     where |     where | ||||||
|         D: Deserializer<'de>, |         D: Deserializer<'de>, | ||||||
|     { |     { | ||||||
|     fn convert_relation(ev: EventWithRelatesToJsonRepr) -> Option<Relation> { |         fn convert_relation(ev: EventWithRelatesToJsonRepr) -> Relation { | ||||||
|             if let Some(in_reply_to) = ev.relates_to.in_reply_to { |             if let Some(in_reply_to) = ev.relates_to.in_reply_to { | ||||||
|             return Some(Relation::Reply { in_reply_to }); |                 return Relation::Reply { in_reply_to }; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             #[cfg(feature = "unstable-pre-spec")] |             #[cfg(feature = "unstable-pre-spec")] | ||||||
|             if let Some(relation) = ev.relates_to.relation { |             if let Some(relation) = ev.relates_to.relation { | ||||||
|             let relation = match relation { |                 return match relation { | ||||||
|                     RelationJsonRepr::Annotation(a) => Relation::Annotation(a), |                     RelationJsonRepr::Annotation(a) => Relation::Annotation(a), | ||||||
|                     RelationJsonRepr::Reference(r) => Relation::Reference(r), |                     RelationJsonRepr::Reference(r) => Relation::Reference(r), | ||||||
|                     RelationJsonRepr::Replacement(Replacement { event_id }) => { |                     RelationJsonRepr::Replacement(Replacement { event_id }) => { | ||||||
|                         Relation::Replacement(Replacement { event_id }) |                         Relation::Replacement(Replacement { event_id }) | ||||||
|                     } |                     } | ||||||
|                 // FIXME: Maybe we should log this, though at this point we don't even have access
 |                     // FIXME: Maybe we should log this, though at this point we don't even have
 | ||||||
|                 // to the rel_type of the unknown relation.
 |                     // access to the rel_type of the unknown relation.
 | ||||||
|                 RelationJsonRepr::Unknown => return None, |                     RelationJsonRepr::Unknown => Relation::_Custom, | ||||||
|                 }; |                 }; | ||||||
| 
 |  | ||||||
|             return Some(relation); |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         None |             Relation::_Custom | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         EventWithRelatesToJsonRepr::deserialize(deserializer).map(convert_relation) |         EventWithRelatesToJsonRepr::deserialize(deserializer).map(convert_relation) | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| pub fn serialize<S>(relation: &Option<Relation>, serializer: S) -> Result<S::Ok, S::Error> | impl Serialize for Relation { | ||||||
|  |     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||||
|     where |     where | ||||||
|         S: Serializer, |         S: Serializer, | ||||||
|     { |     { | ||||||
|     let relation = match relation { |         let relates_to = match self { | ||||||
|         Some(rel) => rel, |  | ||||||
|         // FIXME: If this crate ends up depending on tracing, emit a warning here.
 |  | ||||||
|         // This code path should not be reachable due to the skip_serializing_if serde attribute
 |  | ||||||
|         // that should be applied together with `with = "relation_serde"`.
 |  | ||||||
|         None => return serializer.serialize_struct("NoRelation", 0)?.end(), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     let json_repr = match relation { |  | ||||||
|             #[cfg(feature = "unstable-pre-spec")] |             #[cfg(feature = "unstable-pre-spec")] | ||||||
|         Relation::Annotation(r) => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr { |             Relation::Annotation(r) => RelatesToJsonRepr { | ||||||
|                 relation: Some(RelationJsonRepr::Annotation(r.clone())), |                 relation: Some(RelationJsonRepr::Annotation(r.clone())), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
|         }), |             }, | ||||||
|             #[cfg(feature = "unstable-pre-spec")] |             #[cfg(feature = "unstable-pre-spec")] | ||||||
|         Relation::Reference(r) => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr { |             Relation::Reference(r) => RelatesToJsonRepr { | ||||||
|                 relation: Some(RelationJsonRepr::Reference(r.clone())), |                 relation: Some(RelationJsonRepr::Reference(r.clone())), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
|         }), |             }, | ||||||
|             #[cfg(feature = "unstable-pre-spec")] |             #[cfg(feature = "unstable-pre-spec")] | ||||||
|         Relation::Replacement(r) => EventWithRelatesToJsonRepr { |             Relation::Replacement(r) => RelatesToJsonRepr { | ||||||
|             relates_to: RelatesToJsonRepr { |  | ||||||
|                 relation: Some(RelationJsonRepr::Replacement(r.clone())), |                 relation: Some(RelationJsonRepr::Replacement(r.clone())), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
|             }, |             }, | ||||||
|         }, |             Relation::Reply { in_reply_to } => { | ||||||
|         Relation::Reply { in_reply_to } => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr { |                 RelatesToJsonRepr { in_reply_to: Some(in_reply_to.clone()), ..Default::default() } | ||||||
|             in_reply_to: Some(in_reply_to.clone()), |             } | ||||||
|             ..Default::default() |             Relation::_Custom => RelatesToJsonRepr::default(), | ||||||
|         }), |  | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|     json_repr.serialize(serializer) |         EventWithRelatesToJsonRepr { relates_to }.serialize(serializer) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Deserialize, Serialize)] | #[derive(Deserialize, Serialize)] | ||||||
| @ -80,12 +72,6 @@ struct EventWithRelatesToJsonRepr { | |||||||
|     relates_to: RelatesToJsonRepr, |     relates_to: RelatesToJsonRepr, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl EventWithRelatesToJsonRepr { |  | ||||||
|     fn new(relates_to: RelatesToJsonRepr) -> Self { |  | ||||||
|         Self { relates_to } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /// Enum modeling the different ways relationships can be expressed in a `m.relates_to` field of an
 | /// Enum modeling the different ways relationships can be expressed in a `m.relates_to` field of an
 | ||||||
| /// event.
 | /// event.
 | ||||||
| #[derive(Default, Deserialize, Serialize)] | #[derive(Default, Deserialize, Serialize)] | ||||||
|  | |||||||
| @ -41,7 +41,7 @@ pub struct MessageEventContent { | |||||||
|     /// Information about related messages for [rich replies].
 |     /// Information about related messages for [rich replies].
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// [rich replies]: https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies
 |     /// [rich replies]: https://matrix.org/docs/spec/client_server/r0.6.1#rich-replies
 | ||||||
|     #[serde(flatten, with = "relation_serde", skip_serializing_if = "Option::is_none")] |     #[serde(flatten, skip_serializing_if = "Option::is_none")] | ||||||
|     pub relates_to: Option<Relation>, |     pub relates_to: Option<Relation>, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -265,6 +265,7 @@ impl From<MessageType> for MessageEventContent { | |||||||
| ///
 | ///
 | ||||||
| /// Currently used for replies and editing (message replacement).
 | /// Currently used for replies and editing (message replacement).
 | ||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
|  | #[allow(clippy::manual_non_exhaustive)] | ||||||
| #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] | #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] | ||||||
| pub enum Relation { | pub enum Relation { | ||||||
|     /// An `m.in_reply_to` relation indicating that the event is a reply to another event.
 |     /// An `m.in_reply_to` relation indicating that the event is a reply to another event.
 | ||||||
| @ -277,6 +278,9 @@ pub enum Relation { | |||||||
|     #[cfg(feature = "unstable-pre-spec")] |     #[cfg(feature = "unstable-pre-spec")] | ||||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))] |     #[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))] | ||||||
|     Replacement(Replacement), |     Replacement(Replacement), | ||||||
|  | 
 | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     _Custom, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// Information about the event a "rich reply" is replying to.
 | /// Information about the event a "rich reply" is replying to.
 | ||||||
|  | |||||||
| @ -3,10 +3,8 @@ | |||||||
| use serde::{de, Deserialize}; | use serde::{de, Deserialize}; | ||||||
| use serde_json::value::RawValue as RawJsonValue; | use serde_json::value::RawValue as RawJsonValue; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use super::{MessageEventContent, MessageType, Relation}; | ||||||
|     from_raw_json_value, | use crate::from_raw_json_value; | ||||||
|     room::message::{MessageEventContent, MessageType}, |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| impl<'de> Deserialize<'de> for MessageEventContent { | impl<'de> Deserialize<'de> for MessageEventContent { | ||||||
|     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||||
| @ -15,10 +13,10 @@ impl<'de> Deserialize<'de> for MessageEventContent { | |||||||
|     { |     { | ||||||
|         let json = Box::<RawJsonValue>::deserialize(deserializer)?; |         let json = Box::<RawJsonValue>::deserialize(deserializer)?; | ||||||
|         let mut deserializer = serde_json::Deserializer::from_str(json.get()); |         let mut deserializer = serde_json::Deserializer::from_str(json.get()); | ||||||
|         let relation = |         let relates_to = | ||||||
|             super::relation_serde::deserialize(&mut deserializer).map_err(de::Error::custom)?; |             Option::<Relation>::deserialize(&mut deserializer).map_err(de::Error::custom)?; | ||||||
| 
 | 
 | ||||||
|         Ok(Self { msgtype: from_raw_json_value(&json)?, relates_to: relation }) |         Ok(Self { msgtype: from_raw_json_value(&json)?, relates_to }) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| #[cfg(feature = "unstable-pre-spec")] | #[cfg(feature = "unstable-pre-spec")] | ||||||
| use ruma_identifiers::EventId; | use ruma_identifiers::EventId; | ||||||
| use serde::{ser::SerializeStruct as _, Deserialize, Deserializer, Serialize, Serializer}; | use serde::{Deserialize, Deserializer, Serialize, Serializer}; | ||||||
| 
 | 
 | ||||||
| #[cfg(feature = "unstable-pre-spec")] | #[cfg(feature = "unstable-pre-spec")] | ||||||
| use super::Replacement; | use super::Replacement; | ||||||
| @ -8,49 +8,42 @@ use super::{InReplyTo, Relation}; | |||||||
| #[cfg(feature = "unstable-pre-spec")] | #[cfg(feature = "unstable-pre-spec")] | ||||||
| use crate::room::message::MessageEventContent; | use crate::room::message::MessageEventContent; | ||||||
| 
 | 
 | ||||||
| pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Relation>, D::Error> | impl<'de> Deserialize<'de> for Relation { | ||||||
|  |     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||||||
|     where |     where | ||||||
|         D: Deserializer<'de>, |         D: Deserializer<'de>, | ||||||
|     { |     { | ||||||
|     fn convert_relation(ev: EventWithRelatesToJsonRepr) -> Option<Relation> { |         let ev = EventWithRelatesToJsonRepr::deserialize(deserializer)?; | ||||||
|  | 
 | ||||||
|         if let Some(in_reply_to) = ev.relates_to.in_reply_to { |         if let Some(in_reply_to) = ev.relates_to.in_reply_to { | ||||||
|             return Some(Relation::Reply { in_reply_to }); |             return Ok(Relation::Reply { in_reply_to }); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         #[cfg(feature = "unstable-pre-spec")] |         #[cfg(feature = "unstable-pre-spec")] | ||||||
|         if let Some(relation) = ev.relates_to.relation { |         if let Some(relation) = ev.relates_to.relation { | ||||||
|             let relation = match relation { |             return Ok(match relation { | ||||||
|                 RelationJsonRepr::Replacement(ReplacementJsonRepr { event_id }) => { |                 RelationJsonRepr::Replacement(ReplacementJsonRepr { event_id }) => { | ||||||
|                     let new_content = ev.new_content?; |                     let new_content = ev | ||||||
|  |                         .new_content | ||||||
|  |                         .ok_or_else(|| serde::de::Error::missing_field("m.new_content"))?; | ||||||
|                     Relation::Replacement(Replacement { event_id, new_content }) |                     Relation::Replacement(Replacement { event_id, new_content }) | ||||||
|                 } |                 } | ||||||
|                 // FIXME: Maybe we should log this, though at this point we don't even have access
 |                 // FIXME: Maybe we should log this, though at this point we don't even have
 | ||||||
|                 // to the rel_type of the unknown relation.
 |                 // access to the rel_type of the unknown relation.
 | ||||||
|                 RelationJsonRepr::Unknown => return None, |                 RelationJsonRepr::Unknown => Relation::_Custom, | ||||||
|             }; |             }); | ||||||
| 
 |  | ||||||
|             return Some(relation); |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         None |         Ok(Relation::_Custom) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     EventWithRelatesToJsonRepr::deserialize(deserializer).map(convert_relation) | impl Serialize for Relation { | ||||||
| } |     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||||
| 
 |  | ||||||
| pub fn serialize<S>(relation: &Option<Relation>, serializer: S) -> Result<S::Ok, S::Error> |  | ||||||
|     where |     where | ||||||
|         S: Serializer, |         S: Serializer, | ||||||
|     { |     { | ||||||
|     let relation = match relation { |         let json_repr = match self { | ||||||
|         Some(rel) => rel, |  | ||||||
|         // FIXME: If this crate ends up depending on tracing, emit a warning here.
 |  | ||||||
|         // This code path should not be reachable due to the skip_serializing_if serde attribute
 |  | ||||||
|         // that should be applied together with `with = "relation_serde"`.
 |  | ||||||
|         None => return serializer.serialize_struct("NoRelation", 0)?.end(), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     let json_repr = match relation { |  | ||||||
|             Relation::Reply { in_reply_to } => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr { |             Relation::Reply { in_reply_to } => EventWithRelatesToJsonRepr::new(RelatesToJsonRepr { | ||||||
|                 in_reply_to: Some(in_reply_to.clone()), |                 in_reply_to: Some(in_reply_to.clone()), | ||||||
|                 ..Default::default() |                 ..Default::default() | ||||||
| @ -67,12 +60,14 @@ where | |||||||
|                     new_content: Some(new_content.clone()), |                     new_content: Some(new_content.clone()), | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |             Relation::_Custom => EventWithRelatesToJsonRepr::default(), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         json_repr.serialize(serializer) |         json_repr.serialize(serializer) | ||||||
|     } |     } | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| #[derive(Deserialize, Serialize)] | #[derive(Default, Deserialize, Serialize)] | ||||||
| struct EventWithRelatesToJsonRepr { | struct EventWithRelatesToJsonRepr { | ||||||
|     #[serde(rename = "m.relates_to", default, skip_serializing_if = "RelatesToJsonRepr::is_empty")] |     #[serde(rename = "m.relates_to", default, skip_serializing_if = "RelatesToJsonRepr::is_empty")] | ||||||
|     relates_to: RelatesToJsonRepr, |     relates_to: RelatesToJsonRepr, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user