some derive cleanup

This commit is contained in:
Robin Appelman 2023-03-07 22:44:05 +01:00
commit c06e09e691
5 changed files with 58 additions and 49 deletions

4
.gitignore vendored
View file

@ -1,5 +1,5 @@
target target
/result result
/.direnv .direnv
Cargo.lock Cargo.lock
callgrind.* callgrind.*

View file

@ -21,10 +21,12 @@ logos = "0.12"
memchr = "2.5.0" memchr = "2.5.0"
ahash = "0.8.3" ahash = "0.8.3"
tf-log-parser-derive = { version = "0.1", path = "./derive" } tf-log-parser-derive = { version = "0.1", path = "./derive" }
miette = "5.5.0"
[dev-dependencies] [dev-dependencies]
criterion = "0.4" criterion = "0.4"
iai = "0.1" iai = "0.1"
miette = { version = "5.5.0", features = ["fancy"] }
[[bench]] [[bench]]
name = "bench" name = "bench"

View file

@ -1,19 +1,10 @@
use crate::{Derivable, DeriveParams}; use crate::{err, Derivable, DeriveParams};
use proc_macro2::{Ident, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, quote_spanned}; use quote::{quote, quote_spanned};
use syn::{Data, DeriveInput, Error, Field, Fields, Generics, Lifetime, Result, Type, TypePath}; use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Field, Fields, Generics, Lifetime, Result, Type, TypePath};
use syn_util::{contains_attribute, get_attribute_value}; use syn_util::{contains_attribute, get_attribute_value};
macro_rules! bail {
($msg:expr $(,)?) => {
return Err(Error::new(Span::call_site(), &$msg[..]))
};
( $msg:expr => $span_to_blame:expr $(,)? ) => {
return Err(Error::new_spanned(&$span_to_blame, $msg))
};
}
pub struct Event; pub struct Event;
impl Derivable for Event { impl Derivable for Event {
@ -29,30 +20,10 @@ impl Derivable for Event {
let required_fields = required_params let required_fields = required_params
.map(|param| { .map(|param| {
let field_name = &param.field_name; let field_name = &param.field_name;
let parser = param.parser();
let after = param.skip_after();
let parser = match (&param.param_name, param.quoted) { Ok(quote_spanned!(param.span() =>
(Some(param_name), true) => {
quote_spanned!(field_name.span() => param_parse_with(#param_name, quoted(parse_field)))
}
(Some(param_name), false) => {
quote_spanned!(field_name.span() => param_parse_with(#param_name, parse_field))
}
(None, true) => {
quote_spanned!(field_name.span() => quoted(parse_field))
}
(None, false) => {
quote_spanned!(field_name.span() => parse_field)
}
};
let skip_after = param.skip_after as usize;
let after = if skip_after > 0 {
quote_spanned!(field_name.span() => let input = &input[#skip_after..];)
} else {
quote!()
};
Ok(quote_spanned!(field_name.span() =>
#[allow(unused_variables)] #[allow(unused_variables)]
let (input, #field_name) = #parser(input)?; let (input, #field_name) = #parser(input)?;
#after #after
@ -79,7 +50,7 @@ impl Derivable for Event {
.map(|param| { .map(|param| {
let field_name = &param.field_name; let field_name = &param.field_name;
let Some(param_name) = param.param_name.as_deref() else { let Some(param_name) = param.param_name.as_deref() else {
bail!("optional fields can't be unnamed" => param.field_name) return err("optional fields can't be unnamed", &param.field_name);
}; };
let parser = if param.quoted { let parser = if param.quoted {
@ -136,10 +107,10 @@ pub struct EventParams {
impl DeriveParams for EventParams { impl DeriveParams for EventParams {
fn parse(input: &DeriveInput) -> Result<EventParams> { fn parse(input: &DeriveInput) -> Result<EventParams> {
let Data::Struct(data) = &input.data else { let Data::Struct(data) = &input.data else {
bail!("only supported on structs" => input) return err("only supported on structs", input);
}; };
let Fields::Named(fields) = &data.fields else { let Fields::Named(fields) = &data.fields else {
bail!("only supported with named fields" => input) return err("only supported with named fields", input);
}; };
let name = input.ident.clone(); let name = input.ident.clone();
let generics = input.generics.clone(); let generics = input.generics.clone();
@ -152,7 +123,10 @@ impl DeriveParams for EventParams {
let mut last_optional = false; let mut last_optional = false;
for param in params.iter() { for param in params.iter() {
if last_optional > param.optional { if last_optional > param.optional {
bail!("optional fields are required to be at the end" => param.field_name) return err(
"optional fields are required to be at the end",
&param.field_name,
);
} }
last_optional = param.optional; last_optional = param.optional;
} }
@ -162,14 +136,13 @@ impl DeriveParams for EventParams {
{ {
Lifetime::new(&lifetime, name.span()) Lifetime::new(&lifetime, name.span())
} else { } else {
let mut lifetimes = input.generics.lifetimes(); let mut lifetimes = input.generics.lifetimes().cloned();
let lifetime = lifetimes let lifetime = lifetimes
.next() .next()
.cloned()
.map(|lifetime| lifetime.lifetime) .map(|lifetime| lifetime.lifetime)
.unwrap_or_else(|| Lifetime::new("'_", name.span())); .unwrap_or_else(|| Lifetime::new("'_", name.span()));
if lifetimes.next().is_some() { if lifetimes.next().is_some() {
bail!("For structs with more than one lifetime, manually specifiying the lifetime is required" => name); return err("For structs with more than one lifetime, manually specifiying the lifetime is required", name);
} }
lifetime lifetime
}; };
@ -185,6 +158,7 @@ impl DeriveParams for EventParams {
#[derive(Debug)] #[derive(Debug)]
pub struct EventParam { pub struct EventParam {
span: Span,
field_name: Ident, field_name: Ident,
param_name: Option<String>, param_name: Option<String>,
optional: bool, optional: bool,
@ -218,11 +192,12 @@ impl EventParam {
get_attribute_value(&input.attrs, &["event", "skip_after"]).unwrap_or_default(); get_attribute_value(&input.attrs, &["event", "skip_after"]).unwrap_or_default();
if optional && skip_after > 0 { if optional && skip_after > 0 {
bail!("skip_after can't be used with optional fields" => input); return err("skip_after can't be used with optional fields", input);
} }
let quoted = contains_attribute(&input.attrs, &["event", "quoted"]); let quoted = contains_attribute(&input.attrs, &["event", "quoted"]);
Ok(EventParam { Ok(EventParam {
span: input.span(),
field_name, field_name,
param_name, param_name,
optional, optional,
@ -230,4 +205,31 @@ impl EventParam {
quoted, quoted,
}) })
} }
fn span(&self) -> Span {
self.span
}
fn parser(&self) -> TokenStream {
let field_parser = if self.quoted {
quote_spanned!(self.span() => quoted(parse_field))
} else {
quote_spanned!(self.span() => parse_field)
};
if let Some(param_name) = &self.param_name {
quote_spanned!(self.span() => param_parse_with(#param_name, #field_parser))
} else {
quote_spanned!(self.span() => #field_parser)
}
}
fn skip_after(&self) -> TokenStream {
let skip_after = self.skip_after as usize;
if skip_after > 0 {
quote_spanned!(self.span() => let input = &input.get(#skip_after..).ok_or(nom::Err::Incomplete(nom::Needed::Unknown))?;)
} else {
quote!()
}
}
} }

View file

@ -6,8 +6,9 @@ mod event;
use crate::event::Event; use crate::event::Event;
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use std::fmt::Debug; use quote::ToTokens;
use syn::{parse_macro_input, DeriveInput, Result}; use std::fmt::{Debug, Display};
use syn::{parse_macro_input, DeriveInput, Error, Result};
/// Derive the `Event` trait for a struct /// Derive the `Event` trait for a struct
#[proc_macro_derive(Event, attributes(event))] #[proc_macro_derive(Event, attributes(event))]
@ -36,3 +37,7 @@ trait Derivable {
trait DeriveParams: Sized + Debug { trait DeriveParams: Sized + Debug {
fn parse(input: &DeriveInput) -> Result<Self>; fn parse(input: &DeriveInput) -> Result<Self>;
} }
fn err<R, T: ToTokens, U: Display>(msg: U, span: T) -> Result<R> {
return Err(Error::new_spanned(&span, msg));
}

View file

@ -160,7 +160,7 @@ impl<'a> Iterator for LineSplit<'a> {
None if self.start < self.input.len() => { None if self.start < self.input.len() => {
let line = &self.input[self.start..]; let line = &self.input[self.start..];
self.start = self.input.len(); self.start = self.input.len();
Some(dbg!(line.trim_end_matches("\n"))) Some(line.trim_end_matches("\n"))
} }
_ => None, _ => None,
} }