Combine and sort common and custom fields using a Vec<Field>.
This commit is contained in:
parent
2f54ee3e32
commit
a5834ea192
179
src/gen.rs
179
src/gen.rs
@ -2,7 +2,12 @@
|
||||
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{Attribute, Field, Ident};
|
||||
use syn::{
|
||||
parse::{self, Parse, ParseStream},
|
||||
parse_quote,
|
||||
punctuated::Punctuated,
|
||||
Attribute, Field, Ident, Token,
|
||||
};
|
||||
|
||||
use crate::parse::{EventKind, RumaEventInput};
|
||||
|
||||
@ -14,34 +19,99 @@ pub struct RumaEvent {
|
||||
/// The name of the type of the event's `content` field.
|
||||
content_name: Ident,
|
||||
|
||||
/// Additional named struct fields in the top level event struct.
|
||||
fields: Option<Vec<Field>>,
|
||||
/// Struct fields of the event.
|
||||
fields: Vec<Field>,
|
||||
|
||||
/// The kind of event.
|
||||
#[allow(dead_code)]
|
||||
kind: EventKind,
|
||||
|
||||
/// The name of the event.
|
||||
name: Ident,
|
||||
}
|
||||
|
||||
impl RumaEvent {
|
||||
/// Fills in the event's struct definition with fields common to all basic events.
|
||||
fn common_basic_event_fields(&self) -> TokenStream {
|
||||
impl From<RumaEventInput> for RumaEvent {
|
||||
fn from(input: RumaEventInput) -> Self {
|
||||
let kind = input.kind;
|
||||
let name = input.name;
|
||||
let content_name = Ident::new(&format!("{}Content", &name), Span::call_site());
|
||||
|
||||
let mut fields = match kind {
|
||||
EventKind::Event => {
|
||||
populate_event_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new))
|
||||
}
|
||||
EventKind::RoomEvent => populate_room_event_fields(
|
||||
content_name.clone(),
|
||||
input.fields.unwrap_or_else(Vec::new),
|
||||
),
|
||||
EventKind::StateEvent => {
|
||||
populate_state_fields(content_name.clone(), input.fields.unwrap_or_else(Vec::new))
|
||||
}
|
||||
};
|
||||
|
||||
fields.sort_unstable_by_key(|field| field.ident.clone().unwrap());
|
||||
|
||||
Self {
|
||||
attrs: input.attrs,
|
||||
content_name,
|
||||
fields,
|
||||
kind,
|
||||
name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for RumaEvent {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let attrs = &self.attrs;
|
||||
let content_name = &self.content_name;
|
||||
|
||||
quote! {
|
||||
let event_fields = &self.fields;
|
||||
|
||||
let name = &self.name;
|
||||
|
||||
let content_docstring = format!("The payload for `{}`.", name);
|
||||
|
||||
let output = quote!(
|
||||
#(#attrs),*
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct #name {
|
||||
#(#event_fields),*
|
||||
}
|
||||
|
||||
#[doc = #content_docstring]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct #content_name {
|
||||
}
|
||||
);
|
||||
|
||||
output.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills in the event's struct definition with fields common to all basic events.
|
||||
fn populate_event_fields(content_name: Ident, mut fields: Vec<Field>) -> Vec<Field> {
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||
/// The event's content.
|
||||
pub content: #content_name,
|
||||
};
|
||||
|
||||
let mut additional_fields = Vec::with_capacity(punctuated_fields.len());
|
||||
|
||||
for punctuated_field in punctuated_fields {
|
||||
additional_fields.push(punctuated_field.field);
|
||||
}
|
||||
|
||||
fields.extend(additional_fields);
|
||||
|
||||
fields
|
||||
}
|
||||
|
||||
/// Fills in the event's struct definition with fields common to all room events.
|
||||
fn common_room_event_fields(&self) -> TokenStream {
|
||||
let common_basic_event_fields = self.common_basic_event_fields();
|
||||
|
||||
quote! {
|
||||
#common_basic_event_fields
|
||||
fn populate_room_event_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
|
||||
let mut fields = populate_event_fields(content_name, fields);
|
||||
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||
/// The unique identifier for the event.
|
||||
pub event_id: ruma_identifiers::EventId,
|
||||
|
||||
@ -57,72 +127,55 @@ impl RumaEvent {
|
||||
|
||||
/// The unique identifier for the user who sent this event.
|
||||
pub sender: ruma_identifiers::UserId,
|
||||
};
|
||||
|
||||
let mut additional_fields = Vec::with_capacity(punctuated_fields.len());
|
||||
|
||||
for punctuated_field in punctuated_fields {
|
||||
additional_fields.push(punctuated_field.field);
|
||||
}
|
||||
|
||||
fields.extend(additional_fields);
|
||||
|
||||
fields
|
||||
}
|
||||
|
||||
/// Fills in the event's struct definition with fields common to all state events.
|
||||
fn common_state_event_fields(&self) -> TokenStream {
|
||||
let content_name = &self.content_name;
|
||||
let common_room_event_fields = self.common_room_event_fields();
|
||||
|
||||
quote! {
|
||||
#common_room_event_fields
|
||||
fn populate_state_fields(content_name: Ident, fields: Vec<Field>) -> Vec<Field> {
|
||||
let mut fields = populate_room_event_fields(content_name.clone(), fields);
|
||||
|
||||
let punctuated_fields: Punctuated<ParsableNamedField, Token![,]> = parse_quote! {
|
||||
/// The previous content for this state key, if any.
|
||||
pub prev_content: Option<#content_name>,
|
||||
|
||||
/// A key that determines which piece of room state the event represents.
|
||||
pub state_key: String,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RumaEventInput> for RumaEvent {
|
||||
fn from(input: RumaEventInput) -> Self {
|
||||
Self {
|
||||
attrs: input.attrs,
|
||||
content_name: Ident::new(&format!("{}Content", input.name), Span::call_site()),
|
||||
fields: input.fields,
|
||||
kind: input.kind,
|
||||
name: input.name,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for RumaEvent {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let attrs = &self.attrs;
|
||||
let content_name = &self.content_name;
|
||||
|
||||
let common_event_fields = match self.kind {
|
||||
EventKind::Event => self.common_basic_event_fields(),
|
||||
EventKind::RoomEvent => self.common_room_event_fields(),
|
||||
EventKind::StateEvent => self.common_state_event_fields(),
|
||||
};
|
||||
|
||||
let event_fields = match &self.fields {
|
||||
Some(fields) => fields.clone(),
|
||||
None => vec![],
|
||||
};
|
||||
let mut additional_fields = Vec::with_capacity(punctuated_fields.len());
|
||||
|
||||
let name = &self.name;
|
||||
|
||||
let content_docstring = format!("The payload for `{}`.", name);
|
||||
|
||||
let output = quote!(
|
||||
#(#attrs),*
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct #name {
|
||||
#common_event_fields
|
||||
#(#event_fields),*
|
||||
for punctuated_field in punctuated_fields {
|
||||
additional_fields.push(punctuated_field.field);
|
||||
}
|
||||
|
||||
#[doc = #content_docstring]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct #content_name {
|
||||
}
|
||||
);
|
||||
fields.extend(additional_fields);
|
||||
|
||||
output.to_tokens(tokens);
|
||||
fields
|
||||
}
|
||||
|
||||
/// A wrapper around `syn::Field` that makes it possible to parse `Punctuated<Field, Token![,]>`
|
||||
/// from a `TokenStream`.
|
||||
///
|
||||
/// See https://github.com/dtolnay/syn/issues/651 for more context.
|
||||
struct ParsableNamedField {
|
||||
/// The wrapped `Field`.
|
||||
pub field: Field,
|
||||
}
|
||||
|
||||
impl Parse for ParsableNamedField {
|
||||
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
|
||||
let field = Field::parse_named(input)?;
|
||||
|
||||
Ok(Self { field })
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user