Add derive macro for StateEventContent
This commit is contained in:
		
							parent
							
								
									27a9b36499
								
							
						
					
					
						commit
						ef6e2e7023
					
				| @ -80,15 +80,25 @@ impl ToTokens for RumaEvent { | ||||
|         // let attrs = &self.attrs;
 | ||||
|         let content_name = &self.content_name; | ||||
|         // let event_fields = &self.fields;
 | ||||
|         let event_type = &self.event_type; | ||||
| 
 | ||||
|         let name = &self.name; | ||||
|         let content_docstring = format!("The payload for `{}`.", name); | ||||
| 
 | ||||
|         let content = match &self.content { | ||||
|             Content::Struct(fields) => { | ||||
|                 // TODO this will all be removed so this is only temp
 | ||||
|                 let event_content_derive = match self.kind { | ||||
|                     EventKind::StateEvent => quote! { | ||||
|                         #[derive(::ruma_events_macros::StateEventContent)] | ||||
|                         #[ruma_event(type = #event_type)] | ||||
|                     }, | ||||
|                     EventKind::RoomEvent | EventKind::Event => TokenStream::new(), | ||||
|                 }; | ||||
|                 quote! { | ||||
|                     #[doc = #content_docstring] | ||||
|                     #[derive(Clone, Debug, serde::Serialize, ::ruma_events_macros::FromRaw)] | ||||
|                     #[derive(Clone, Debug, ::serde::Serialize, ::ruma_events_macros::FromRaw)] | ||||
|                     #event_content_derive | ||||
|                     pub struct #content_name { | ||||
|                         #(#fields),* | ||||
|                     } | ||||
| @ -105,8 +115,6 @@ impl ToTokens for RumaEvent { | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         // let event_type_name = self.event_type.value();
 | ||||
| 
 | ||||
|         content.to_tokens(tokens); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -36,11 +36,14 @@ use proc_macro::TokenStream; | ||||
| use quote::ToTokens; | ||||
| use syn::{parse_macro_input, DeriveInput}; | ||||
| 
 | ||||
| use self::{from_raw::expand_from_raw, gen::RumaEvent, parse::RumaEventInput}; | ||||
| use self::{ | ||||
|     from_raw::expand_from_raw, gen::RumaEvent, parse::RumaEventInput, state::expand_state_event, | ||||
| }; | ||||
| 
 | ||||
| mod from_raw; | ||||
| mod gen; | ||||
| mod parse; | ||||
| mod state; | ||||
| 
 | ||||
| // A note about the `example` modules that appears in doctests:
 | ||||
| //
 | ||||
| @ -145,3 +148,12 @@ pub fn derive_from_raw(input: TokenStream) -> TokenStream { | ||||
|         .unwrap_or_else(|err| err.to_compile_error()) | ||||
|         .into() | ||||
| } | ||||
| 
 | ||||
| /// Generates an implementation of `ruma_events::StateEventContent` and it's super traits.
 | ||||
| #[proc_macro_derive(StateEventContent, attributes(ruma_event))] | ||||
| pub fn derive_state_event_content(input: TokenStream) -> TokenStream { | ||||
|     let input = parse_macro_input!(input as DeriveInput); | ||||
|     expand_state_event(input) | ||||
|         .unwrap_or_else(|err| err.to_compile_error()) | ||||
|         .into() | ||||
| } | ||||
|  | ||||
| @ -15,7 +15,7 @@ pub struct RumaEventInput { | ||||
|     /// The name of the event.
 | ||||
|     pub name: Ident, | ||||
| 
 | ||||
|     /// The kind of event, determiend by the `kind` field.
 | ||||
|     /// The kind of event, determined by the `kind` field.
 | ||||
|     pub kind: EventKind, | ||||
| 
 | ||||
|     /// The value for the `type` field in the JSON representation of this event. There needs to be a
 | ||||
|  | ||||
							
								
								
									
										78
									
								
								ruma-events-macros/src/state.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								ruma-events-macros/src/state.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| //! Implementation of the `StateEventContent` derive macro
 | ||||
| 
 | ||||
| use proc_macro2::{Span, TokenStream}; | ||||
| use quote::quote; | ||||
| use syn::{ | ||||
|     parse::{Parse, ParseStream}, | ||||
|     DeriveInput, LitStr, Token, | ||||
| }; | ||||
| 
 | ||||
| /// Parses attributes for `*EventContent` derives.
 | ||||
| ///
 | ||||
| /// `#[ruma_event(type = "m.room.alias")]`
 | ||||
| enum EventMeta { | ||||
|     /// Variant holds the "m.room.whatever" event type.
 | ||||
|     Type(LitStr), | ||||
| } | ||||
| 
 | ||||
| impl Parse for EventMeta { | ||||
|     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||
|         input.parse::<Token![type]>()?; | ||||
|         input.parse::<Token![=]>()?; | ||||
|         Ok(EventMeta::Type(input.parse::<LitStr>()?)) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Create a `StateEventContent` implementation for a struct
 | ||||
| pub fn expand_state_event(input: DeriveInput) -> syn::Result<TokenStream> { | ||||
|     let ident = &input.ident; | ||||
| 
 | ||||
|     let event_type_attr = input | ||||
|         .attrs | ||||
|         .iter() | ||||
|         .find(|attr| attr.path.is_ident("ruma_event")) | ||||
|         .ok_or_else(|| { | ||||
|             let msg = "no event type attribute found, \ | ||||
|             add `#[ruma_events(type = \"any.room.event\")]` \ | ||||
|             below the event content derive";
 | ||||
| 
 | ||||
|             syn::Error::new(Span::call_site(), msg) | ||||
|         })?; | ||||
| 
 | ||||
|     let event_type = { | ||||
|         let event_meta = event_type_attr.parse_args::<EventMeta>()?; | ||||
|         let EventMeta::Type(lit) = event_meta; | ||||
|         lit | ||||
|     }; | ||||
| 
 | ||||
|     let event_content_impl = quote! { | ||||
|         impl ::ruma_events::EventContent for #ident { | ||||
|             fn event_type(&self) -> &str { | ||||
|                 #event_type | ||||
|             } | ||||
| 
 | ||||
|             fn from_parts( | ||||
|                 ev_type: &str, | ||||
|                 content: Box<::serde_json::value::RawValue> | ||||
|             ) -> Result<Self, ::ruma_events::InvalidEvent> { | ||||
|                 if ev_type != #event_type { | ||||
|                     return Err(::ruma_events::InvalidEvent { | ||||
|                         kind: ::ruma_events::error::InvalidEventKind::Deserialization, | ||||
|                         message: format!("expected `{}` found {}", #event_type, ev_type), | ||||
|                     }); | ||||
|                 } | ||||
| 
 | ||||
|                 let ev_json = ::ruma_events::EventJson::from(content); | ||||
|                 ev_json.deserialize() | ||||
|             } | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     Ok(quote! { | ||||
|         #event_content_impl | ||||
| 
 | ||||
|         impl ::ruma_events::RoomEventContent for #ident { } | ||||
| 
 | ||||
|         impl ::ruma_events::StateEventContent for #ident { } | ||||
|     }) | ||||
| } | ||||
| @ -20,25 +20,3 @@ ruma_event! { | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl EventContent for AliasesEventContent { | ||||
|     fn event_type(&self) -> &str { | ||||
|         "m.room.aliases" | ||||
|     } | ||||
| 
 | ||||
|     fn from_parts(event_type: &str, content: Box<RawJsonValue>) -> Result<Self, InvalidEvent> { | ||||
|         if event_type != "m.room.aliases" { | ||||
|             return Err(InvalidEvent { | ||||
|                 kind: InvalidEventKind::Deserialization, | ||||
|                 message: format!("expected `m.room.aliases` found {}", event_type), | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         let ev_json = EventJson::from(content); | ||||
|         ev_json.deserialize() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl RoomEventContent for AliasesEventContent {} | ||||
| 
 | ||||
| impl StateEventContent for AliasesEventContent {} | ||||
|  | ||||
| @ -27,25 +27,3 @@ ruma_event! { | ||||
|         }, | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl EventContent for AvatarEventContent { | ||||
|     fn event_type(&self) -> &str { | ||||
|         "m.room.avatar" | ||||
|     } | ||||
| 
 | ||||
|     fn from_parts(event_type: &str, content: Box<RawJsonValue>) -> Result<Self, InvalidEvent> { | ||||
|         if event_type != "m.room.avatar" { | ||||
|             return Err(InvalidEvent { | ||||
|                 kind: InvalidEventKind::Deserialization, | ||||
|                 message: format!("expected `m.room.avatar` found {}", event_type), | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         let ev_json = EventJson::from(content); | ||||
|         ev_json.deserialize() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl RoomEventContent for AvatarEventContent {} | ||||
| 
 | ||||
| impl StateEventContent for AvatarEventContent {} | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user