syn2 + structmeta

This commit is contained in:
Robin Appelman 2023-04-01 22:58:47 +02:00
commit a6b5e67ce8
4 changed files with 64 additions and 23 deletions

View file

@ -8,10 +8,10 @@ name = "tf_log_parser_derive"
proc-macro = true proc-macro = true
[dependencies] [dependencies]
syn = "1.0" syn = "2.0.12"
quote = "1.0" quote = "1.0.26"
proc-macro2 = "1.0" proc-macro2 = "1.0.54"
syn_util = "0.4" structmeta = "0.2.0"
[dev-dependencies] [dev-dependencies]
tf-log-parser = { version = "0.1", path = ".." } tf-log-parser = { version = "0.1", path = ".." }

View file

@ -1,9 +1,12 @@
use crate::{err, Derivable, DeriveParams}; use crate::{err, Derivable, DeriveParams};
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use structmeta::StructMeta;
use syn::spanned::Spanned; use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Field, Fields, Generics, Lifetime, Result, Type, TypePath}; use syn::{
use syn_util::{contains_attribute, get_attribute_value}; Attribute, Data, DeriveInput, Field, Fields, Generics, Lifetime, LitBool, LitInt, LitStr,
Result, Type, TypePath,
};
pub struct Event; pub struct Event;
@ -107,7 +110,6 @@ impl Derivable for Event {
} }
} }
#[derive(Debug)]
pub struct EventParams { pub struct EventParams {
name: Ident, name: Ident,
lifetime: Lifetime, lifetime: Lifetime,
@ -117,6 +119,7 @@ pub struct EventParams {
impl DeriveParams for EventParams { impl DeriveParams for EventParams {
fn parse(input: &DeriveInput) -> Result<EventParams> { fn parse(input: &DeriveInput) -> Result<EventParams> {
let attrs: EventAttr = parse_attrs(&input.attrs);
let Data::Struct(data) = &input.data else { let Data::Struct(data) = &input.data else {
return err("only supported on structs", input); return err("only supported on structs", input);
}; };
@ -142,10 +145,8 @@ impl DeriveParams for EventParams {
last_optional = param.optional; last_optional = param.optional;
} }
let lifetime = if let Some(lifetime) = let lifetime = if let Some(lifetime) = attrs.lifetime {
get_attribute_value::<String>(&input.attrs, &["event", "lifetime"]) lifetime
{
Lifetime::new(&lifetime, name.span())
} else { } else {
let mut lifetimes = input.generics.lifetimes().cloned(); let mut lifetimes = input.generics.lifetimes().cloned();
let lifetime = lifetimes let lifetime = lifetimes
@ -167,7 +168,6 @@ impl DeriveParams for EventParams {
} }
} }
#[derive(Debug)]
pub struct EventParam { pub struct EventParam {
span: Span, span: Span,
field_name: Ident, field_name: Ident,
@ -180,12 +180,15 @@ pub struct EventParam {
impl EventParam { impl EventParam {
pub fn parse(input: &Field) -> Result<EventParam> { pub fn parse(input: &Field) -> Result<EventParam> {
let attrs: EventAttr = parse_attrs(&input.attrs);
let field_name = input.ident.clone().expect("no name on named fields"); let field_name = input.ident.clone().expect("no name on named fields");
let param_name = if contains_attribute(&input.attrs, &["event", "unnamed"]) { let param_name = if attrs.unnamed {
None None
} else { } else {
Some( Some(
get_attribute_value(&input.attrs, &["event", "name"]) attrs
.name
.map(|lit| lit.value())
.unwrap_or_else(|| field_name.to_string()), .unwrap_or_else(|| field_name.to_string()),
) )
}; };
@ -199,16 +202,20 @@ impl EventParam {
} }
_ => false, _ => false,
}; };
let optional = is_option || contains_attribute(&input.attrs, &["event", "default"]); let optional = is_option || attrs.default;
let skip_after = let skip_after = attrs
get_attribute_value(&input.attrs, &["event", "skip_after"]).unwrap_or_default(); .skip_after
.and_then(|lit| lit.base10_parse().ok())
.unwrap_or_default();
if optional && skip_after > 0 { if optional && skip_after > 0 {
return err("skip_after can't be used with optional fields", input); return err("skip_after can't be used with optional fields", input);
} }
let quoted = let quoted = attrs
get_attribute_value(&input.attrs, &["event", "quoted"]).unwrap_or(param_name.is_none()); .quoted
let subject = contains_attribute(&input.attrs, &["event", "subject"]); .map(|lit| lit.value)
.unwrap_or(param_name.is_none());
let subject = attrs.subject;
Ok(EventParam { Ok(EventParam {
span: input.span(), span: input.span(),
@ -253,3 +260,38 @@ impl EventParam {
} }
} }
} }
fn parse_attrs(attrs: &[Attribute]) -> EventAttr {
let mut result = EventAttr::default();
for attr in attrs {
if let Ok(parsed) = attr.parse_args() {
result = result.merge(parsed);
}
}
result
}
#[derive(Default, StructMeta)]
struct EventAttr {
quoted: Option<LitBool>,
subject: bool,
default: bool,
unnamed: bool,
skip_after: Option<LitInt>,
name: Option<LitStr>,
lifetime: Option<Lifetime>,
}
impl EventAttr {
fn merge(self, other: Self) -> Self {
Self {
quoted: self.quoted.or(other.quoted),
subject: self.subject || other.subject,
default: self.default || other.default,
unnamed: self.unnamed || other.unnamed,
skip_after: self.skip_after.or(other.skip_after),
name: self.name.or(other.name),
lifetime: self.lifetime.or(other.lifetime),
}
}
}

View file

@ -38,7 +38,6 @@ impl Derivable for Events {
} }
} }
#[derive(Debug)]
pub struct EventsParam { pub struct EventsParam {
name: Ident, name: Ident,
generics: Generics, generics: Generics,

View file

@ -9,7 +9,7 @@ use crate::event::Event;
use crate::events::Events; use crate::events::Events;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::ToTokens; use quote::ToTokens;
use std::fmt::{Debug, Display}; use std::fmt::Display;
use syn::{parse_macro_input, DeriveInput, Error, Result}; use syn::{parse_macro_input, DeriveInput, Error, Result};
/// Derive the `Event` trait for a struct /// Derive the `Event` trait for a struct
@ -44,7 +44,7 @@ trait Derivable {
fn derive(params: Self::Params) -> Result<TokenStream>; fn derive(params: Self::Params) -> Result<TokenStream>;
} }
trait DeriveParams: Sized + Debug { trait DeriveParams: Sized {
fn parse(input: &DeriveInput) -> Result<Self>; fn parse(input: &DeriveInput) -> Result<Self>;
} }