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" {
|
||||
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" {
|
||||
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 {
|
||||
quote_spanned! {field_span=>
|
||||
@ -35,7 +35,7 @@ pub fn expand_from_raw(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||
});
|
||||
|
||||
Ok(quote! {
|
||||
impl crate::FromRaw for #ident {
|
||||
impl ::ruma_events::FromRaw for #ident {
|
||||
type Raw = raw::#ident;
|
||||
|
||||
fn from_raw(raw: raw::#ident) -> Self {
|
||||
|
@ -92,7 +92,7 @@ impl ToTokens for RumaEvent {
|
||||
|
||||
let event_type = if self.is_custom {
|
||||
quote! {
|
||||
crate::EventType::Custom(self.event_type.clone())
|
||||
::ruma_events::EventType::Custom(self.event_type.clone())
|
||||
}
|
||||
} else {
|
||||
let event_type = &self.event_type;
|
||||
@ -189,7 +189,7 @@ impl ToTokens for RumaEvent {
|
||||
};
|
||||
|
||||
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 {
|
||||
EventKind::RoomEvent | EventKind::StateEvent => {
|
||||
quote! {
|
||||
impl crate::RoomEvent for #name {
|
||||
impl ::ruma_events::RoomEvent for #name {
|
||||
/// The unique identifier for the event.
|
||||
fn event_id(&self) -> &ruma_identifiers::EventId {
|
||||
&self.event_id
|
||||
@ -251,7 +251,7 @@ impl ToTokens for RumaEvent {
|
||||
|
||||
let impl_state_event = if self.kind == EventKind::StateEvent {
|
||||
quote! {
|
||||
impl crate::StateEvent for #name {
|
||||
impl ::ruma_events::StateEvent for #name {
|
||||
/// The previous content for this state key, if any.
|
||||
fn prev_content(&self) -> Option<&Self::Content> {
|
||||
self.prev_content.as_ref()
|
||||
@ -284,7 +284,7 @@ impl ToTokens for RumaEvent {
|
||||
}
|
||||
|
||||
quote! {
|
||||
impl crate::FromRaw for #content_name {
|
||||
impl ::ruma_events::FromRaw for #content_name {
|
||||
type Raw = raw::#content_name;
|
||||
|
||||
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.
|
||||
type Content = #content_name;
|
||||
|
||||
@ -347,7 +347,7 @@ impl ToTokens for RumaEvent {
|
||||
}
|
||||
|
||||
/// The type of the event.
|
||||
fn event_type(&self) -> crate::EventType {
|
||||
fn event_type(&self) -> ::ruma_events::EventType {
|
||||
#event_type
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ impl Parse for RumaEventInput {
|
||||
|
||||
let mut punctuated = Punctuated::new();
|
||||
punctuated.push(PathSegment {
|
||||
ident: Ident::new("crate", Span::call_site()),
|
||||
ident: Ident::new("ruma_events", Span::call_site()),
|
||||
arguments: PathArguments::None,
|
||||
});
|
||||
punctuated.push(PathSegment {
|
||||
@ -119,7 +119,7 @@ impl Parse for RumaEventInput {
|
||||
punctuated.push(variant.clone());
|
||||
|
||||
event_type = Some(Path {
|
||||
leading_colon: None,
|
||||
leading_colon: Some(Default::default()),
|
||||
segments: punctuated,
|
||||
});
|
||||
}
|
||||
|
@ -135,7 +135,12 @@ mod macros;
|
||||
mod algorithm;
|
||||
mod event_type;
|
||||
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;
|
||||
/// 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 crate::TryFromRaw;
|
||||
use crate::{EventResult, TryFromRaw};
|
||||
|
||||
pub fn try_convert_variant<Enum: TryFromRaw, Content: TryFromRaw>(
|
||||
variant: fn(Content) -> Enum,
|
||||
@ -107,35 +112,25 @@ pub fn default_true() -> bool {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_util {
|
||||
use std::fmt::Debug;
|
||||
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
use crate::{EventResult, TryFromRaw};
|
||||
|
||||
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()
|
||||
);
|
||||
}
|
||||
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());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub use test_util::*;
|
||||
// This would be #[cfg(test)] if it wasn't used from external tests
|
||||
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::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
convert::{Infallible, TryFrom},
|
||||
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
||||
};
|
||||
use std::{collections::HashMap, convert::TryFrom};
|
||||
|
||||
use js_int::UInt;
|
||||
use ruma_events::util::serde_json_eq_try_from_raw;
|
||||
use ruma_events_macros::ruma_event;
|
||||
use ruma_identifiers::{EventId, RoomAliasId, RoomId, UserId};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize};
|
||||
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`
|
||||
mod common_case {
|
||||
use super::*;
|
Loading…
x
Reference in New Issue
Block a user