Import some things from ruma-events
This commit is contained in:
parent
1a00c1d386
commit
f664a0132d
89
src/empty.rs
Normal file
89
src/empty.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use std::fmt::{self, Formatter};
|
||||
|
||||
use serde::{
|
||||
de::{Deserialize, Deserializer, MapAccess, Visitor},
|
||||
ser::{Serialize, SerializeMap, Serializer},
|
||||
};
|
||||
|
||||
/// A meaningless value that serializes to an empty JSON object.
|
||||
///
|
||||
/// This type is used in a few places where the Matrix specification requires an empty JSON object,
|
||||
/// but it's wasteful to represent it as a `BTreeMap` in Rust code.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Empty;
|
||||
|
||||
impl Serialize for Empty {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_map(Some(0))?.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Empty {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct EmptyMapVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for EmptyMapVisitor {
|
||||
type Value = Empty;
|
||||
|
||||
fn expecting(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "an object/map")
|
||||
}
|
||||
|
||||
fn visit_map<A>(self, _map: A) -> Result<Self::Value, A::Error>
|
||||
where
|
||||
A: MapAccess<'de>,
|
||||
{
|
||||
Ok(Empty)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(EmptyMapVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serde serialization and deserialization functions that map a `Vec<T>` to a
|
||||
/// `BTreeMap<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 std::collections::BTreeMap;
|
||||
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use super::Empty;
|
||||
|
||||
#[allow(clippy::ptr_arg)]
|
||||
pub fn serialize<S, T>(
|
||||
vec: &Vec<T>,
|
||||
serializer: S,
|
||||
) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
T: Serialize + Eq + Ord,
|
||||
{
|
||||
vec.iter()
|
||||
.map(|v| (v, Empty))
|
||||
.collect::<BTreeMap<_, _>>()
|
||||
.serialize(serializer)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
T: Deserialize<'de> + Eq + Ord,
|
||||
{
|
||||
BTreeMap::<T, Empty>::deserialize(deserializer)
|
||||
.map(|hashmap| hashmap.into_iter().map(|(k, _)| k).collect())
|
||||
}
|
||||
}
|
55
src/lib.rs
55
src/lib.rs
@ -1,10 +1,65 @@
|
||||
//! De-/serialization helpers for other ruma crates
|
||||
|
||||
use serde::de::{Deserialize, IntoDeserializer};
|
||||
|
||||
pub mod duration;
|
||||
pub mod empty;
|
||||
pub mod json_string;
|
||||
pub mod time;
|
||||
pub mod urlencoded;
|
||||
|
||||
pub use empty::vec_as_map_of_empty;
|
||||
|
||||
/// Check whether a value is equal to its default value.
|
||||
pub fn is_default<T: Default + PartialEq>(val: &T) -> bool {
|
||||
val == &T::default()
|
||||
}
|
||||
|
||||
/// Simply returns `true`.
|
||||
///
|
||||
/// Useful for `#[serde(default = ...)]`.
|
||||
pub fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// 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)?;
|
||||
// TODO: Switch to and remove this attribute `opt.as_deref()` once MSRV is >= 1.40
|
||||
#[allow(clippy::option_as_ref_deref, clippy::unknown_clippy_lints)]
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[cfg(test)]
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn serde_json_eq<T>(de: T, se: serde_json::Value)
|
||||
where
|
||||
T: Clone + Debug + PartialEq + Serialize + DeserializeOwned,
|
||||
{
|
||||
assert_eq!(se, serde_json::to_value(de.clone()).unwrap());
|
||||
assert_eq!(de, serde_json::from_value(se).unwrap());
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user