Split impl FromRaw
generation into its own macro
This commit is contained in:
parent
0efe82bf88
commit
016659a99c
48
ruma-events-macros/src/from_raw.rs
Normal file
48
ruma-events-macros/src/from_raw.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//! Implementation of the `FromRaw` derive macro
|
||||||
|
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::{quote, quote_spanned};
|
||||||
|
use syn::{spanned::Spanned, Data, DeriveInput, Fields};
|
||||||
|
|
||||||
|
/// Create a `FromRaw` implementation for a struct
|
||||||
|
pub fn expand_from_raw(input: DeriveInput) -> syn::Result<TokenStream> {
|
||||||
|
let fields = match input.data {
|
||||||
|
Data::Struct(s) => match s.fields {
|
||||||
|
Fields::Named(fs) => fs.named,
|
||||||
|
_ => panic!("#[derive(FromRaw)] only supports structs with named fields!"),
|
||||||
|
},
|
||||||
|
_ => panic!("#[derive(FromRaw)] only supports structs!"),
|
||||||
|
};
|
||||||
|
let ident = &input.ident;
|
||||||
|
|
||||||
|
let init_list = fields.iter().map(|field| {
|
||||||
|
let field_ident = field.ident.as_ref().unwrap();
|
||||||
|
let field_span = field.span();
|
||||||
|
|
||||||
|
if field_ident == "content" {
|
||||||
|
quote_spanned! {field_span=>
|
||||||
|
content: crate::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),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
quote_spanned! {field_span=>
|
||||||
|
#field_ident: raw.#field_ident,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl crate::FromRaw for #ident {
|
||||||
|
type Raw = raw::#ident;
|
||||||
|
|
||||||
|
fn from_raw(raw: raw::#ident) -> Self {
|
||||||
|
Self {
|
||||||
|
#(#init_list)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -148,7 +148,6 @@ impl ToTokens for RumaEvent {
|
|||||||
// are `Some` in order to increase the number of fields we tell serde to serialize.
|
// are `Some` in order to increase the number of fields we tell serde to serialize.
|
||||||
let mut optional_field_idents = Vec::with_capacity(event_fields.len());
|
let mut optional_field_idents = Vec::with_capacity(event_fields.len());
|
||||||
|
|
||||||
let mut try_from_field_values: Vec<TokenStream> = Vec::with_capacity(event_fields.len());
|
|
||||||
let mut serialize_field_calls: Vec<TokenStream> = Vec::with_capacity(event_fields.len());
|
let mut serialize_field_calls: Vec<TokenStream> = Vec::with_capacity(event_fields.len());
|
||||||
|
|
||||||
for field in event_fields {
|
for field in event_fields {
|
||||||
@ -162,40 +161,6 @@ impl ToTokens for RumaEvent {
|
|||||||
|
|
||||||
let span = field.span();
|
let span = field.span();
|
||||||
|
|
||||||
let try_from_field_value = if ident == "content" {
|
|
||||||
match &self.content {
|
|
||||||
Content::Struct(_) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
content: crate::FromRaw::from_raw(raw.content),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Content::Typedef(_) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
content: raw.content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ident == "prev_content" {
|
|
||||||
match &self.content {
|
|
||||||
Content::Struct(_) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
prev_content: raw.prev_content.map(crate::FromRaw::from_raw),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Content::Typedef(_) => {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
prev_content: raw.prev_content,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
quote_spanned! {span=>
|
|
||||||
#ident: raw.#ident,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
try_from_field_values.push(try_from_field_value);
|
|
||||||
|
|
||||||
// Does the same thing as #[serde(skip_serializing_if = "Option::is_none")]
|
// Does the same thing as #[serde(skip_serializing_if = "Option::is_none")]
|
||||||
let serialize_field_call = if is_option(&field.ty) {
|
let serialize_field_call = if is_option(&field.ty) {
|
||||||
optional_field_idents.push(ident.clone());
|
optional_field_idents.push(ident.clone());
|
||||||
@ -341,23 +306,13 @@ impl ToTokens for RumaEvent {
|
|||||||
|
|
||||||
let output = quote!(
|
let output = quote!(
|
||||||
#(#attrs)*
|
#(#attrs)*
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug, ruma_events_macros::FromRaw)]
|
||||||
pub struct #name {
|
pub struct #name {
|
||||||
#(#stripped_fields),*
|
#(#stripped_fields),*
|
||||||
}
|
}
|
||||||
|
|
||||||
#content
|
#content
|
||||||
|
|
||||||
impl crate::FromRaw for #name {
|
|
||||||
type Raw = raw::#name;
|
|
||||||
|
|
||||||
fn from_raw(raw: raw::#name) -> Self {
|
|
||||||
Self {
|
|
||||||
#(#try_from_field_values)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#impl_event_result_compatible_for_content
|
#impl_event_result_compatible_for_content
|
||||||
|
|
||||||
impl serde::Serialize for #name {
|
impl serde::Serialize for #name {
|
||||||
|
@ -34,9 +34,11 @@ extern crate proc_macro;
|
|||||||
|
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
|
|
||||||
use crate::{gen::RumaEvent, parse::RumaEventInput};
|
use self::{from_raw::expand_from_raw, gen::RumaEvent, parse::RumaEventInput};
|
||||||
|
|
||||||
|
mod from_raw;
|
||||||
mod gen;
|
mod gen;
|
||||||
mod parse;
|
mod parse;
|
||||||
|
|
||||||
@ -132,3 +134,14 @@ pub fn ruma_event(input: TokenStream) -> TokenStream {
|
|||||||
|
|
||||||
ruma_event.into_token_stream().into()
|
ruma_event.into_token_stream().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates an implementation of `ruma_events::FromRaw`. Only usable inside of `ruma_events`.
|
||||||
|
/// Requires there to be a `raw` module in the same scope, with a type with the same name and fields
|
||||||
|
/// as the one that this macro is used on.
|
||||||
|
#[proc_macro_derive(FromRaw)]
|
||||||
|
pub fn derive_from_raw(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
expand_from_raw(input)
|
||||||
|
.unwrap_or_else(|err| err.to_compile_error())
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
collections::HashMap,
|
||||||
convert::{Infallible, TryFrom},
|
convert::{Infallible, TryFrom},
|
||||||
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
fmt::{Debug, Display, Formatter, Result as FmtResult},
|
||||||
};
|
};
|
||||||
@ -103,6 +104,25 @@ pub trait TryFromRaw: Sized {
|
|||||||
fn try_from_raw(_: Self::Raw) -> Result<Self, Self::Err>;
|
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 {
|
impl<T: FromRaw> TryFromRaw for T {
|
||||||
type Raw = <T as FromRaw>::Raw;
|
type Raw = <T as FromRaw>::Raw;
|
||||||
type Err = Infallible;
|
type Err = Infallible;
|
||||||
@ -472,7 +492,7 @@ mod type_alias {
|
|||||||
///
|
///
|
||||||
/// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered
|
/// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered
|
||||||
/// *direct* for that particular user.
|
/// *direct* for that particular user.
|
||||||
std::collections::HashMap<ruma_identifiers::UserId, Vec<ruma_identifiers::RoomId>>
|
HashMap<ruma_identifiers::UserId, Vec<ruma_identifiers::RoomId>>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::{convert::Infallible, fmt::Display};
|
use std::{collections::HashMap, convert::Infallible, fmt::Display};
|
||||||
|
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
|
|
||||||
@ -27,6 +27,25 @@ pub trait TryFromRaw: Sized {
|
|||||||
fn try_from_raw(_: Self::Raw) -> Result<Self, Self::Err>;
|
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 {
|
impl<T: FromRaw> TryFromRaw for T {
|
||||||
type Raw = <T as FromRaw>::Raw;
|
type Raw = <T as FromRaw>::Raw;
|
||||||
type Err = Infallible;
|
type Err = Infallible;
|
||||||
|
@ -350,6 +350,14 @@ impl<'de> Deserialize<'de> for Empty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromRaw for Empty {
|
||||||
|
type Raw = Self;
|
||||||
|
|
||||||
|
fn from_raw(raw: Self) -> Self {
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A basic event.
|
/// A basic event.
|
||||||
pub trait Event: Debug + Serialize + Sized + TryFromRaw {
|
pub trait Event: Debug + Serialize + Sized + TryFromRaw {
|
||||||
/// The type of this event's `content` field.
|
/// The type of this event's `content` field.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user