Simplify proc_macro tests
… by moving them from ruma-events-macros to ruma-events and updating the macros to work in different contexts (without items like FromRaw being available at the calling crate's root)
This commit is contained in:
parent
016659a99c
commit
0c6dd0ba32
@ -21,11 +21,11 @@ pub fn expand_from_raw(input: DeriveInput) -> syn::Result<TokenStream> {
|
|||||||
|
|
||||||
if field_ident == "content" {
|
if field_ident == "content" {
|
||||||
quote_spanned! {field_span=>
|
quote_spanned! {field_span=>
|
||||||
content: crate::FromRaw::from_raw(raw.content),
|
content: ::ruma_events::FromRaw::from_raw(raw.content),
|
||||||
}
|
}
|
||||||
} else if field_ident == "prev_content" {
|
} else if field_ident == "prev_content" {
|
||||||
quote_spanned! {field_span=>
|
quote_spanned! {field_span=>
|
||||||
prev_content: raw.prev_content.map(crate::FromRaw::from_raw),
|
prev_content: raw.prev_content.map(::ruma_events::FromRaw::from_raw),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
quote_spanned! {field_span=>
|
quote_spanned! {field_span=>
|
||||||
@ -35,7 +35,7 @@ pub fn expand_from_raw(input: DeriveInput) -> syn::Result<TokenStream> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl crate::FromRaw for #ident {
|
impl ::ruma_events::FromRaw for #ident {
|
||||||
type Raw = raw::#ident;
|
type Raw = raw::#ident;
|
||||||
|
|
||||||
fn from_raw(raw: raw::#ident) -> Self {
|
fn from_raw(raw: raw::#ident) -> Self {
|
||||||
|
@ -92,7 +92,7 @@ impl ToTokens for RumaEvent {
|
|||||||
|
|
||||||
let event_type = if self.is_custom {
|
let event_type = if self.is_custom {
|
||||||
quote! {
|
quote! {
|
||||||
crate::EventType::Custom(self.event_type.clone())
|
::ruma_events::EventType::Custom(self.event_type.clone())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let event_type = &self.event_type;
|
let event_type = &self.event_type;
|
||||||
@ -189,7 +189,7 @@ impl ToTokens for RumaEvent {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let import_event_in_serialize_impl = quote! {
|
let import_event_in_serialize_impl = quote! {
|
||||||
use crate::Event as _;
|
use ::ruma_events::Event as _;
|
||||||
};
|
};
|
||||||
|
|
||||||
(
|
(
|
||||||
@ -214,7 +214,7 @@ impl ToTokens for RumaEvent {
|
|||||||
let impl_room_event = match self.kind {
|
let impl_room_event = match self.kind {
|
||||||
EventKind::RoomEvent | EventKind::StateEvent => {
|
EventKind::RoomEvent | EventKind::StateEvent => {
|
||||||
quote! {
|
quote! {
|
||||||
impl crate::RoomEvent for #name {
|
impl ::ruma_events::RoomEvent for #name {
|
||||||
/// The unique identifier for the event.
|
/// The unique identifier for the event.
|
||||||
fn event_id(&self) -> &ruma_identifiers::EventId {
|
fn event_id(&self) -> &ruma_identifiers::EventId {
|
||||||
&self.event_id
|
&self.event_id
|
||||||
@ -251,7 +251,7 @@ impl ToTokens for RumaEvent {
|
|||||||
|
|
||||||
let impl_state_event = if self.kind == EventKind::StateEvent {
|
let impl_state_event = if self.kind == EventKind::StateEvent {
|
||||||
quote! {
|
quote! {
|
||||||
impl crate::StateEvent for #name {
|
impl ::ruma_events::StateEvent for #name {
|
||||||
/// The previous content for this state key, if any.
|
/// The previous content for this state key, if any.
|
||||||
fn prev_content(&self) -> Option<&Self::Content> {
|
fn prev_content(&self) -> Option<&Self::Content> {
|
||||||
self.prev_content.as_ref()
|
self.prev_content.as_ref()
|
||||||
@ -284,7 +284,7 @@ impl ToTokens for RumaEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
impl crate::FromRaw for #content_name {
|
impl ::ruma_events::FromRaw for #content_name {
|
||||||
type Raw = raw::#content_name;
|
type Raw = raw::#content_name;
|
||||||
|
|
||||||
fn from_raw(
|
fn from_raw(
|
||||||
@ -337,7 +337,7 @@ impl ToTokens for RumaEvent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crate::Event for #name {
|
impl ::ruma_events::Event for #name {
|
||||||
/// The type of this event's `content` field.
|
/// The type of this event's `content` field.
|
||||||
type Content = #content_name;
|
type Content = #content_name;
|
||||||
|
|
||||||
@ -347,7 +347,7 @@ impl ToTokens for RumaEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The type of the event.
|
/// The type of the event.
|
||||||
fn event_type(&self) -> crate::EventType {
|
fn event_type(&self) -> ::ruma_events::EventType {
|
||||||
#event_type
|
#event_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,7 +109,7 @@ impl Parse for RumaEventInput {
|
|||||||
|
|
||||||
let mut punctuated = Punctuated::new();
|
let mut punctuated = Punctuated::new();
|
||||||
punctuated.push(PathSegment {
|
punctuated.push(PathSegment {
|
||||||
ident: Ident::new("crate", Span::call_site()),
|
ident: Ident::new("ruma_events", Span::call_site()),
|
||||||
arguments: PathArguments::None,
|
arguments: PathArguments::None,
|
||||||
});
|
});
|
||||||
punctuated.push(PathSegment {
|
punctuated.push(PathSegment {
|
||||||
@ -119,7 +119,7 @@ impl Parse for RumaEventInput {
|
|||||||
punctuated.push(variant.clone());
|
punctuated.push(variant.clone());
|
||||||
|
|
||||||
event_type = Some(Path {
|
event_type = Some(Path {
|
||||||
leading_colon: None,
|
leading_colon: Some(Default::default()),
|
||||||
segments: punctuated,
|
segments: punctuated,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,12 @@ mod macros;
|
|||||||
mod algorithm;
|
mod algorithm;
|
||||||
mod event_type;
|
mod event_type;
|
||||||
mod from_raw;
|
mod from_raw;
|
||||||
mod util;
|
#[doc(hidden)] // only public for external tests
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
|
// Hack to allow both ruma-events itself and external crates (or tests) to use procedural macros
|
||||||
|
// that expect `ruma_events` to exist in the prelude.
|
||||||
|
extern crate self as ruma_events;
|
||||||
|
|
||||||
pub mod call;
|
pub mod call;
|
||||||
/// Enums for heterogeneous collections of events.
|
/// Enums for heterogeneous collections of events.
|
||||||
|
59
src/util.rs
59
src/util.rs
@ -1,7 +1,12 @@
|
|||||||
use serde::de::{Deserialize, DeserializeOwned, IntoDeserializer};
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
use serde::{
|
||||||
|
de::{Deserialize, DeserializeOwned, IntoDeserializer},
|
||||||
|
Serialize,
|
||||||
|
};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::TryFromRaw;
|
use crate::{EventResult, TryFromRaw};
|
||||||
|
|
||||||
pub fn try_convert_variant<Enum: TryFromRaw, Content: TryFromRaw>(
|
pub fn try_convert_variant<Enum: TryFromRaw, Content: TryFromRaw>(
|
||||||
variant: fn(Content) -> Enum,
|
variant: fn(Content) -> Enum,
|
||||||
@ -107,35 +112,25 @@ pub fn default_true() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_util {
|
pub fn serde_json_eq<T>(de: T, se: serde_json::Value)
|
||||||
use std::fmt::Debug;
|
where
|
||||||
|
T: Clone + Debug + PartialEq + Serialize + DeserializeOwned,
|
||||||
use serde::{de::DeserializeOwned, Serialize};
|
{
|
||||||
|
assert_eq!(se, serde_json::to_value(de.clone()).unwrap());
|
||||||
use crate::{EventResult, TryFromRaw};
|
assert_eq!(de, serde_json::from_value(se).unwrap());
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serde_json_eq_try_from_raw<T>(de: T, se: serde_json::Value)
|
|
||||||
where
|
|
||||||
T: Clone + Debug + PartialEq + Serialize + TryFromRaw,
|
|
||||||
{
|
|
||||||
assert_eq!(se, serde_json::to_value(de.clone()).unwrap());
|
|
||||||
assert_eq!(
|
|
||||||
de,
|
|
||||||
serde_json::from_value::<EventResult<_>>(se)
|
|
||||||
.unwrap()
|
|
||||||
.into_result()
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
// This would be #[cfg(test)] if it wasn't used from external tests
|
||||||
pub use test_util::*;
|
pub fn serde_json_eq_try_from_raw<T>(de: T, se: serde_json::Value)
|
||||||
|
where
|
||||||
|
T: Clone + Debug + PartialEq + Serialize + TryFromRaw,
|
||||||
|
{
|
||||||
|
assert_eq!(se, serde_json::to_value(de.clone()).unwrap());
|
||||||
|
assert_eq!(
|
||||||
|
de,
|
||||||
|
serde_json::from_value::<EventResult<_>>(se)
|
||||||
|
.unwrap()
|
||||||
|
.into_result()
|
||||||
|
.unwrap()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,274 +1,11 @@
|
|||||||
use std::{
|
use std::{collections::HashMap, convert::TryFrom};
|
||||||
borrow::Cow,
|
|
||||||
collections::HashMap,
|
|
||||||
convert::{Infallible, TryFrom},
|
|
||||||
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
|
||||||
};
|
|
||||||
|
|
||||||
use js_int::UInt;
|
use js_int::UInt;
|
||||||
|
use ruma_events::util::serde_json_eq_try_from_raw;
|
||||||
use ruma_events_macros::ruma_event;
|
use ruma_events_macros::ruma_event;
|
||||||
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
|
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
|
||||||
/// The type of an event.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
// Cow<str> because deserialization sometimes needs to copy to unescape things
|
|
||||||
#[serde(from = "Cow<'_, str>", into = "String")]
|
|
||||||
pub enum EventType {
|
|
||||||
/// m.direct
|
|
||||||
Direct,
|
|
||||||
|
|
||||||
/// m.room.aliases
|
|
||||||
RoomAliases,
|
|
||||||
|
|
||||||
/// m.room.redaction
|
|
||||||
RoomRedaction,
|
|
||||||
|
|
||||||
/// Any event that is not part of the specification.
|
|
||||||
Custom(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for EventType {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|
||||||
let event_type_str = match *self {
|
|
||||||
EventType::Direct => "m.direct",
|
|
||||||
EventType::RoomAliases => "m.room.aliases",
|
|
||||||
EventType::RoomRedaction => "m.room.redaction",
|
|
||||||
EventType::Custom(ref event_type) => event_type,
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(f, "{}", event_type_str)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<Cow<'a, str>> for EventType {
|
|
||||||
fn from(s: Cow<'a, str>) -> EventType {
|
|
||||||
match &s as &str {
|
|
||||||
"m.direct" => EventType::Direct,
|
|
||||||
"m.room.aliases" => EventType::RoomAliases,
|
|
||||||
"m.room.redaction" => EventType::RoomRedaction,
|
|
||||||
_ => EventType::Custom(s.into_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&str> for EventType {
|
|
||||||
fn from(s: &str) -> EventType {
|
|
||||||
EventType::from(Cow::Borrowed(s))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EventType> for String {
|
|
||||||
fn from(event_type: EventType) -> String {
|
|
||||||
event_type.to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The result of deserializing an event, which may or may not be valid.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum EventResult<T: TryFromRaw> {
|
|
||||||
/// `T` deserialized and validated successfully.
|
|
||||||
Ok(T),
|
|
||||||
|
|
||||||
/// `T` deserialized but was invalid.
|
|
||||||
///
|
|
||||||
/// `InvalidEvent` contains the original input.
|
|
||||||
Err(InvalidEvent),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: TryFromRaw> EventResult<T> {
|
|
||||||
/// Convert `EventResult<T>` into the equivalent `std::result::Result<T, InvalidEvent>`.
|
|
||||||
pub fn into_result(self) -> Result<T, InvalidEvent> {
|
|
||||||
match self {
|
|
||||||
EventResult::Ok(t) => Ok(t),
|
|
||||||
EventResult::Err(invalid_event) => Err(invalid_event),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Marks types that can be deserialized as EventResult<Self> (and don't need fallible conversion
|
|
||||||
/// from their raw type)
|
|
||||||
pub trait FromRaw: Sized {
|
|
||||||
/// The raw form of this event that deserialization falls back to if deserializing `Self` fails.
|
|
||||||
type Raw: DeserializeOwned;
|
|
||||||
|
|
||||||
fn from_raw(_: Self::Raw) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait TryFromRaw: Sized {
|
|
||||||
/// The raw form of this event that deserialization falls back to if deserializing `Self` fails.
|
|
||||||
type Raw: DeserializeOwned;
|
|
||||||
type Err: Display;
|
|
||||||
|
|
||||||
fn try_from_raw(_: Self::Raw) -> Result<Self, Self::Err>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromRaw for serde_json::Value {
|
|
||||||
type Raw = Self;
|
|
||||||
|
|
||||||
fn from_raw(raw: Self) -> Self {
|
|
||||||
raw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<K, V, S> FromRaw for HashMap<K, V, S>
|
|
||||||
where
|
|
||||||
Self: DeserializeOwned,
|
|
||||||
{
|
|
||||||
type Raw = Self;
|
|
||||||
|
|
||||||
fn from_raw(raw: Self) -> Self {
|
|
||||||
raw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: FromRaw> TryFromRaw for T {
|
|
||||||
type Raw = <T as FromRaw>::Raw;
|
|
||||||
type Err = Infallible;
|
|
||||||
|
|
||||||
fn try_from_raw(raw: Self::Raw) -> Result<Self, Self::Err> {
|
|
||||||
Ok(Self::from_raw(raw))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de, T> Deserialize<'de> for EventResult<T>
|
|
||||||
where
|
|
||||||
T: TryFromRaw,
|
|
||||||
{
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let json = serde_json::Value::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
let raw_data: T::Raw = match serde_json::from_value(json.clone()) {
|
|
||||||
Ok(raw) => raw,
|
|
||||||
Err(error) => {
|
|
||||||
return Ok(EventResult::Err(InvalidEvent {
|
|
||||||
json,
|
|
||||||
message: error.to_string(),
|
|
||||||
kind: InvalidEventKind::Deserialization,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match T::try_from_raw(raw_data) {
|
|
||||||
Ok(value) => Ok(EventResult::Ok(value)),
|
|
||||||
Err(err) => Ok(EventResult::Err(InvalidEvent {
|
|
||||||
message: err.to_string(),
|
|
||||||
json,
|
|
||||||
kind: InvalidEventKind::Validation,
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A basic event.
|
|
||||||
pub trait Event: Debug + Serialize + TryFromRaw {
|
|
||||||
/// The type of this event's `content` field.
|
|
||||||
type Content: Debug + Serialize;
|
|
||||||
|
|
||||||
/// The event's content.
|
|
||||||
fn content(&self) -> &Self::Content;
|
|
||||||
|
|
||||||
/// The type of the event.
|
|
||||||
fn event_type(&self) -> EventType;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An event within the context of a room.
|
|
||||||
pub trait RoomEvent: Event {
|
|
||||||
/// The unique identifier for the event.
|
|
||||||
fn event_id(&self) -> &ruma_identifiers::EventId;
|
|
||||||
|
|
||||||
/// Timestamp (milliseconds since the UNIX epoch) on originating homeserver when this event was
|
|
||||||
/// sent.
|
|
||||||
fn origin_server_ts(&self) -> js_int::UInt;
|
|
||||||
|
|
||||||
/// The unique identifier for the room associated with this event.
|
|
||||||
///
|
|
||||||
/// This can be `None` if the event came from a context where there is
|
|
||||||
/// no ambiguity which room it belongs to, like a `/sync` response for example.
|
|
||||||
fn room_id(&self) -> Option<&ruma_identifiers::RoomId>;
|
|
||||||
|
|
||||||
/// The unique identifier for the user who sent this event.
|
|
||||||
fn sender(&self) -> &ruma_identifiers::UserId;
|
|
||||||
|
|
||||||
/// Additional key-value pairs not signed by the homeserver.
|
|
||||||
fn unsigned(&self) -> Option<&serde_json::Value>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An event that describes persistent state about a room.
|
|
||||||
pub trait StateEvent: RoomEvent {
|
|
||||||
/// The previous content for this state key, if any.
|
|
||||||
fn prev_content(&self) -> Option<&Self::Content>;
|
|
||||||
|
|
||||||
/// A key that determines which piece of room state the event represents.
|
|
||||||
fn state_key(&self) -> &str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An event that is malformed or otherwise invalid.
|
|
||||||
///
|
|
||||||
/// When attempting to deserialize an `EventResult`, an error in the input data may cause
|
|
||||||
/// deserialization to fail, or the JSON structure may be correct, but additional constraints
|
|
||||||
/// defined in the matrix specification are not upheld. This type provides an error message and a
|
|
||||||
/// `serde_json::Value` representation of the invalid event, as well as a flag for which type of
|
|
||||||
/// error was encountered.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct InvalidEvent {
|
|
||||||
message: String,
|
|
||||||
json: Value,
|
|
||||||
kind: InvalidEventKind,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
enum InvalidEventKind {
|
|
||||||
Deserialization,
|
|
||||||
Validation,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InvalidEvent {
|
|
||||||
/// A message describing why the event is invalid.
|
|
||||||
pub fn message(&self) -> String {
|
|
||||||
self.message.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The `serde_json::Value` representation of the invalid event.
|
|
||||||
pub fn json(&self) -> &Value {
|
|
||||||
&self.json
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether this is a deserialization error.
|
|
||||||
pub fn is_deserialization(&self) -> bool {
|
|
||||||
self.kind == InvalidEventKind::Deserialization
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether this is a validation error.
|
|
||||||
pub fn is_validation(&self) -> bool {
|
|
||||||
self.kind == InvalidEventKind::Validation
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for InvalidEvent {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
|
||||||
write!(f, "{}", self.message())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn serde_json_eq_try_from_raw<T>(de: T, se: serde_json::Value)
|
|
||||||
where
|
|
||||||
T: Clone + Debug + PartialEq + Serialize + TryFromRaw,
|
|
||||||
{
|
|
||||||
assert_eq!(se, serde_json::to_value(de.clone()).unwrap());
|
|
||||||
assert_eq!(
|
|
||||||
de,
|
|
||||||
serde_json::from_value::<EventResult<_>>(se)
|
|
||||||
.unwrap()
|
|
||||||
.into_result()
|
|
||||||
.unwrap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// See note about wrapping macro expansion in a module from `src/lib.rs`
|
// See note about wrapping macro expansion in a module from `src/lib.rs`
|
||||||
mod common_case {
|
mod common_case {
|
||||||
use super::*;
|
use super::*;
|
Loading…
x
Reference in New Issue
Block a user