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.
|
||||
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());
|
||||
|
||||
for field in event_fields {
|
||||
@ -162,40 +161,6 @@ impl ToTokens for RumaEvent {
|
||||
|
||||
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")]
|
||||
let serialize_field_call = if is_option(&field.ty) {
|
||||
optional_field_idents.push(ident.clone());
|
||||
@ -341,23 +306,13 @@ impl ToTokens for RumaEvent {
|
||||
|
||||
let output = quote!(
|
||||
#(#attrs)*
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
#[derive(Clone, PartialEq, Debug, ruma_events_macros::FromRaw)]
|
||||
pub struct #name {
|
||||
#(#stripped_fields),*
|
||||
}
|
||||
|
||||
#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 serde::Serialize for #name {
|
||||
|
@ -34,9 +34,11 @@ extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
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 parse;
|
||||
|
||||
@ -132,3 +134,14 @@ pub fn ruma_event(input: TokenStream) -> TokenStream {
|
||||
|
||||
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::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
convert::{Infallible, TryFrom},
|
||||
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>;
|
||||
}
|
||||
|
||||
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;
|
||||
@ -472,7 +492,7 @@ mod type_alias {
|
||||
///
|
||||
/// A mapping of `UserId`'s to a collection of `RoomId`'s which are considered
|
||||
/// *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;
|
||||
|
||||
@ -27,6 +27,25 @@ pub trait TryFromRaw: Sized {
|
||||
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;
|
||||
|
@ -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.
|
||||
pub trait Event: Debug + Serialize + Sized + TryFromRaw {
|
||||
/// The type of this event's `content` field.
|
||||
|
Loading…
x
Reference in New Issue
Block a user