Skip to content

Update syn to 2.0 #119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions influxdb_derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ repository = "https://github.com/influxdb-rs/influxdb-rust"
proc-macro = true

[dependencies]
proc-macro2 = "1.0.9"
quote = "1.0.3"
syn = { version = "1.0.16", features = ["extra-traits", "full"] }
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["extra-traits", "full"] }
13 changes: 5 additions & 8 deletions influxdb_derive/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;

mod writeable;
use syn::parse_macro_input;
use writeable::expand_writeable;

fn krate() -> TokenStream2 {
quote!(::influxdb)
}

#[proc_macro_derive(InfluxDbWriteable, attributes(influxdb))]
pub fn derive_writeable(tokens: TokenStream) -> TokenStream {
expand_writeable(tokens)
pub fn derive_writeable(input: TokenStream) -> TokenStream {
expand_writeable(parse_macro_input!(input))
.unwrap_or_else(syn::Error::into_compile_error)
.into()
}
173 changes: 97 additions & 76 deletions influxdb_derive/src/writeable.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
use proc_macro::TokenStream;
use proc_macro2::{TokenStream as TokenStream2, TokenTree};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{parse_macro_input, Field, Fields, Ident, ItemStruct};
use std::convert::TryFrom;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
Data, DeriveInput, Field, Fields, Ident, Meta, Token,
};

#[derive(Debug)]
struct WriteableField {
Expand All @@ -10,108 +14,125 @@ struct WriteableField {
is_ignore: bool,
}

impl From<Field> for WriteableField {
fn from(field: Field) -> WriteableField {
let ident = field.ident.expect("fields without ident are not supported");
mod kw {
use syn::custom_keyword;

let check_influx_aware = |attr: &syn::Attribute| -> bool {
attr.path
.segments
.iter()
.last()
.map(|seg| seg.ident.to_string())
.unwrap_or_default()
== "influxdb"
};
custom_keyword!(tag);
custom_keyword!(ignore);
}

let check_for_attr = |token_tree, ident_cmp: &str| -> bool {
match token_tree {
TokenTree::Group(group) => group
.stream()
.into_iter()
.next()
.map(|token_tree| match token_tree {
TokenTree::Ident(ident) => ident == ident_cmp,
_ => false,
})
.unwrap(),
_ => false,
}
};
enum FieldAttr {
Tag(kw::tag),
Ignore(kw::ignore),
}

let is_ignore = field.attrs.iter().any(|attr| {
if !check_influx_aware(attr) {
return false;
}
impl Parse for FieldAttr {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lookahead = input.lookahead1();
if lookahead.peek(kw::tag) {
Ok(Self::Tag(input.parse()?))
} else if lookahead.peek(kw::ignore) {
Ok(Self::Ignore(input.parse()?))
} else {
Err(lookahead.error())
}
}
}

struct FieldAttrs(Punctuated<FieldAttr, Token![,]>);

impl Parse for FieldAttrs {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self(Punctuated::parse_terminated(input)?))
}
}

attr.tokens
.clone()
.into_iter()
.next()
.map(|token_tree| check_for_attr(token_tree, "ignore"))
.unwrap()
});
impl TryFrom<Field> for WriteableField {
type Error = syn::Error;

let is_tag = field.attrs.iter().any(|attr| {
if !check_influx_aware(attr) {
return false;
fn try_from(field: Field) -> syn::Result<WriteableField> {
let ident = field.ident.expect("fields without ident are not supported");
let mut is_tag = false;
let mut is_ignore = false;

for attr in field.attrs {
match attr.meta {
Meta::List(list) if list.path.is_ident("influxdb") => {
for attr in syn::parse2::<FieldAttrs>(list.tokens)?.0 {
match attr {
FieldAttr::Tag(_) => is_tag = true,
FieldAttr::Ignore(_) => is_ignore = true,
}
}
}
_ => {}
}
attr.tokens
.clone()
.into_iter()
.next()
.map(|token_tree| check_for_attr(token_tree, "tag"))
.unwrap()
});
}

WriteableField {
Ok(WriteableField {
ident,
is_tag,
is_ignore,
}
})
}
}

pub fn expand_writeable(tokens: TokenStream) -> TokenStream {
let krate = super::krate();
let input = parse_macro_input!(tokens as ItemStruct);
pub fn expand_writeable(input: DeriveInput) -> syn::Result<TokenStream> {
let ident = input.ident;
let generics = input.generics;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();

let fields = match input.data {
Data::Struct(strukt) => strukt.fields,
Data::Enum(inum) => {
return Err(syn::Error::new(
inum.enum_token.span,
"#[derive(InfluxDbWriteable)] can only be used on structs",
))
}
Data::Union(cdu) => {
return Err(syn::Error::new(
cdu.union_token.span,
"#[derive(InfluxDbWriteable)] can only be used on structs",
))
}
};

let time_field = format_ident!("time");
let time_field_str = time_field.to_string();
#[allow(clippy::cmp_owned)] // that's not how idents work clippy
let fields: Vec<TokenStream2> = match input.fields {
let fields = match fields {
Fields::Named(fields) => fields
.named
.into_iter()
.map(WriteableField::from)
.filter(|field| !field.is_ignore)
.filter(|field| field.ident.to_string() != time_field.to_string())
.map(|field| {
let ident = field.ident;
#[allow(clippy::match_bool)]
match field.is_tag {
true => quote!(query.add_tag(stringify!(#ident), self.#ident)),
false => quote!(query.add_field(stringify!(#ident), self.#ident)),
}
.filter_map(|f| {
WriteableField::try_from(f)
.map(|wf| {
if !wf.is_ignore && wf.ident.to_string() != time_field_str {
let ident = wf.ident;
Some(match wf.is_tag {
true => quote!(query.add_tag(stringify!(#ident), self.#ident)),
false => quote!(query.add_field(stringify!(#ident), self.#ident)),
})
} else {
None
}
})
.transpose()
})
.collect(),
.collect::<syn::Result<Vec<_>>>()?,
_ => panic!("a struct without named fields is not supported"),
};

let output = quote! {
impl #generics #krate::InfluxDbWriteable for #ident #generics
{
fn into_query<I: Into<String>>(self, name : I) -> #krate::WriteQuery
{
let timestamp : #krate::Timestamp = self.#time_field.into();
Ok(quote! {
impl #impl_generics ::influxdb::InfluxDbWriteable for #ident #ty_generics #where_clause {
fn into_query<I: Into<String>>(self, name: I) -> ::influxdb::WriteQuery {
let timestamp: ::influxdb::Timestamp = self.#time_field.into();
let mut query = timestamp.into_query(name);
#(
query = #fields;
)*
query
}
}
};
output.into()
})
}