Move (de)serialization helpers to util module

This commit is contained in:
Jonas Platte 2019-10-22 23:56:03 +02:00
parent 777f3b9686
commit 665fe4f4f4
8 changed files with 72 additions and 72 deletions

View File

@ -3,7 +3,7 @@
use ruma_identifiers::UserId;
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
use crate::{vec_as_map_of_empty, Event as _, EventType, FromRaw};
use crate::{util::vec_as_map_of_empty, Event as _, EventType, FromRaw};
/// A list of users to ignore.
#[derive(Clone, Debug, PartialEq)]

View File

@ -123,7 +123,7 @@ use std::{
use js_int::UInt;
use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{
de::{DeserializeOwned, Error as SerdeError, IntoDeserializer, MapAccess, Visitor},
de::{DeserializeOwned, Error as SerdeError, MapAccess, Visitor},
ser::SerializeMap,
Deserialize, Deserializer, Serialize, Serializer,
};
@ -853,70 +853,6 @@ impl<'de> Deserialize<'de> for Algorithm {
}
}
/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::Deserialize<'de>,
{
let opt = Option::<String>::deserialize(de)?;
let opt = opt.as_ref().map(String::as_str);
match opt {
None | Some("") => Ok(None),
// If T = String, like in m.room.name, the second deserialize is actually superfluous.
// TODO: optimize that somehow?
Some(s) => T::deserialize(s.into_deserializer()).map(Some),
}
}
/// Serde serialization and deserialization functions that map a `Vec<T>` to a `HashMap<T, Empty>`.
///
/// The Matrix spec sometimes specifies lists as hash maps so the list entries can be expanded with
/// attributes without breaking compatibility. As that would be a breaking change for ruma's event
/// types anyway, we convert them to `Vec`s for simplicity, using this module.
///
/// To be used as `#[serde(with = "vec_as_map_of_empty")]`.
mod vec_as_map_of_empty {
use super::Empty;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{collections::HashMap, hash::Hash};
#[allow(clippy::ptr_arg)]
pub fn serialize<S, T>(vec: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize + Hash + Eq,
{
vec.iter()
.map(|v| (v, Empty))
.collect::<HashMap<_, _>>()
.serialize(serializer)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Hash + Eq,
{
HashMap::<T, Empty>::deserialize(deserializer)
.map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect())
}
}
/// Used to default the `bool` fields to `true` during deserialization.
fn default_true() -> bool {
true
}
#[cfg(test)]
mod tests {
use serde_json::{from_str, to_string};

View File

@ -12,7 +12,7 @@ use serde::{
};
use serde_json::{from_value, Value};
use super::{default_true, FromStrError};
use crate::{util::default_true, FromStrError};
ruma_event! {
/// Describes all push rules for a user.

View File

@ -5,7 +5,7 @@ use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
use serde_json::Value;
use crate::{empty_string_as_none, Event, EventType, FromRaw};
use crate::{util::empty_string_as_none, Event, EventType, FromRaw};
/// Informs the room as to which alias is the canonical one.
#[derive(Clone, Debug, PartialEq)]

View File

@ -6,7 +6,7 @@ use ruma_events_macros::ruma_event;
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
use serde::{Deserialize, Serialize};
use crate::default_true;
use crate::util::default_true;
ruma_event! {
/// This is the first event in a room and cannot be changed. It acts as the root of all other

View File

@ -5,7 +5,7 @@ use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
use serde_json::Value;
use crate::{empty_string_as_none, Event as _, EventType, FromRaw, InvalidInput};
use crate::{util::empty_string_as_none, Event as _, EventType, FromRaw, InvalidInput};
/// A human-friendly room name designed to be displayed to the end-user.
#[derive(Clone, Debug, PartialEq)]

View File

@ -5,7 +5,7 @@ use ruma_identifiers::{EventId, RoomId, UserId};
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
use serde_json::Value;
use crate::{default_true, Event as _, EventType, FromRaw};
use crate::{util::default_true, Event as _, EventType, FromRaw};
/// An event to indicate which servers are permitted to participate in the room.
#[derive(Clone, Debug, PartialEq)]

View File

@ -1,4 +1,4 @@
use serde::de::DeserializeOwned;
use serde::de::{Deserialize, DeserializeOwned, IntoDeserializer};
use crate::TryFromRaw;
@ -28,3 +28,67 @@ pub fn get_field<T: DeserializeOwned, E: serde::de::Error>(
)
.map_err(serde_json_error_to_generic_de_error)
}
/// Serde deserialization decorator to map empty Strings to None,
/// and forward non-empty Strings to the Deserialize implementation for T.
/// Useful for the typical
/// "A room with an X event with an absent, null, or empty Y field
/// should be treated the same as a room with no such event."
/// formulation in the spec.
///
/// To be used like this:
/// `#[serde(deserialize_with = "empty_string_as_none"]`
/// Relevant serde issue: https://github.com/serde-rs/serde/issues/1425
pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
where
D: serde::Deserializer<'de>,
T: serde::Deserialize<'de>,
{
let opt = Option::<String>::deserialize(de)?;
let opt = opt.as_ref().map(String::as_str);
match opt {
None | Some("") => Ok(None),
// If T = String, like in m.room.name, the second deserialize is actually superfluous.
// TODO: optimize that somehow?
Some(s) => T::deserialize(s.into_deserializer()).map(Some),
}
}
/// Serde serialization and deserialization functions that map a `Vec<T>` to a `HashMap<T, Empty>`.
///
/// The Matrix spec sometimes specifies lists as hash maps so the list entries can be expanded with
/// attributes without breaking compatibility. As that would be a breaking change for ruma's event
/// types anyway, we convert them to `Vec`s for simplicity, using this module.
///
/// To be used as `#[serde(with = "vec_as_map_of_empty")]`.
pub mod vec_as_map_of_empty {
use crate::Empty;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::{collections::HashMap, hash::Hash};
#[allow(clippy::ptr_arg)]
pub fn serialize<S, T>(vec: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
T: Serialize + Hash + Eq,
{
vec.iter()
.map(|v| (v, Empty))
.collect::<HashMap<_, _>>()
.serialize(serializer)
}
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Hash + Eq,
{
HashMap::<T, Empty>::deserialize(deserializer)
.map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect())
}
}
/// Used to default the `bool` fields to `true` during deserialization.
pub fn default_true() -> bool {
true
}