Skip to content

Commit

Permalink
Update syn to 2.0 (#56)
Browse files Browse the repository at this point in the history
  • Loading branch information
msrd0 committed Mar 20, 2023
1 parent 29c0f6d commit 7d29efe
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 50 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/rust.yml
Expand Up @@ -57,7 +57,7 @@ jobs:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.66"
toolchain: "1.68"
id: rust-toolchain
- uses: actions/cache@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -37,5 +37,5 @@ uuid08 = { package = "uuid", version = "0.8", optional = true }
[dev-dependencies]
paste = "1.0"
pretty_assertions = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde = { version = "1.0.158", features = ["derive"] }
trybuild = "=1.0.79"
6 changes: 3 additions & 3 deletions derive/Cargo.toml
Expand Up @@ -17,6 +17,6 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0"
quote = "1.0"
serde_derive_internals = "0.26"
syn = "1.0"
syn-path = "1.0"
serde_derive_internals = "0.27"
syn = "2.0"
syn-path = "2.0"
42 changes: 27 additions & 15 deletions derive/src/attrs.rs
@@ -1,12 +1,16 @@
use crate::util::ExpectLit;
use syn::{punctuated::Punctuated, spanned::Spanned as _, Attribute, LitStr, Meta, Token};
use proc_macro2::TokenStream;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned as _,
Attribute, LitStr, Meta, Token
};

pub(super) fn parse_doc_attr(input: &Attribute) -> syn::Result<Option<LitStr>> {
input.parse_meta().and_then(|meta| {
Ok(match meta {
Meta::NameValue(kv) => Some(kv.lit.expect_str()?),
_ => None
})
Ok(match &input.meta {
Meta::NameValue(kv) => Some(kv.value.clone().expect_str()?),
_ => None
})
}

Expand Down Expand Up @@ -36,25 +40,33 @@ pub(super) struct ContainerAttributes {
pub(super) deny_unknown_fields: bool
}

struct ParseHelper(Punctuated<Meta, Token![,]>);

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

impl ContainerAttributes {
pub(super) fn parse_from(&mut self, input: &Attribute, error_on_unknown: bool) -> syn::Result<()> {
let tokens: Punctuated<Meta, Token![,]> = input.parse_args_with(Punctuated::parse_terminated)?;
pub(super) fn parse_from(&mut self, tokens: TokenStream, error_on_unknown: bool) -> syn::Result<()> {
let tokens = syn::parse2::<ParseHelper>(tokens)?.0;
for token in tokens {
match token {
Meta::NameValue(kv) if kv.path.is_ident("rename") => {
self.rename = Some(kv.lit.expect_str()?);
self.rename = Some(kv.value.expect_str()?);
},

Meta::NameValue(kv) if kv.path.is_ident("rename_all") => {
self.rename_all = Some(kv.lit.expect_str()?);
self.rename_all = Some(kv.value.expect_str()?);
},

Meta::NameValue(kv) if kv.path.is_ident("tag") => {
self.tag = Some(kv.lit.expect_str()?);
self.tag = Some(kv.value.expect_str()?);
},

Meta::NameValue(kv) if kv.path.is_ident("content") => {
self.content = Some(kv.lit.expect_str()?);
self.content = Some(kv.value.expect_str()?);
},

Meta::Path(path) if path.is_ident("untagged") => {
Expand Down Expand Up @@ -87,12 +99,12 @@ pub(super) struct FieldAttributes {
}

impl FieldAttributes {
pub(super) fn parse_from(&mut self, input: &Attribute, error_on_unknown: bool) -> syn::Result<()> {
let tokens: Punctuated<Meta, Token![,]> = input.parse_args_with(Punctuated::parse_terminated)?;
pub(super) fn parse_from(&mut self, tokens: TokenStream, error_on_unknown: bool) -> syn::Result<()> {
let tokens = syn::parse2::<ParseHelper>(tokens)?.0;
for token in tokens {
match token {
Meta::NameValue(kv) if kv.path.is_ident("rename") => {
self.rename = Some(kv.lit.expect_str()?);
self.rename = Some(kv.value.expect_str()?);
},

Meta::Path(path) if path.is_ident("default") => {
Expand Down
33 changes: 21 additions & 12 deletions derive/src/lib.rs
Expand Up @@ -8,7 +8,7 @@
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, TraitBound, TraitBoundModifier, TypeParamBound};
use syn::{parse_macro_input, Data, DeriveInput, Meta, TraitBound, TraitBoundModifier, TypeParamBound};
use syn_path::path;

mod attrs;
Expand All @@ -28,25 +28,34 @@ pub fn derive_openapi_type(input: TokenStream) -> TokenStream {
expand_openapi_type(input).unwrap_or_else(|err| err.to_compile_error()).into()
}

fn filter_parse_attrs(
attrs: &mut ContainerAttributes,
input: &DeriveInput,
filter: &str,
error_on_unknown: bool
) -> syn::Result<()> {
for attr in &input.attrs {
match &attr.meta {
Meta::List(meta) if meta.path.is_ident(filter) => {
attrs.parse_from(meta.tokens.clone(), error_on_unknown)?;
},
_ => {}
}
}
Ok(())
}

fn expand_openapi_type(mut input: DeriveInput) -> syn::Result<TokenStream2> {
let ident = &input.ident;

// parse #[serde] and #[openapi] attributes
let mut attrs = ContainerAttributes::default();
for attr in &input.attrs {
if attr.path.is_ident("serde") {
attrs.parse_from(attr, false)?;
}
}
for attr in &input.attrs {
if attr.path.is_ident("openapi") {
attrs.parse_from(attr, true)?;
}
}
filter_parse_attrs(&mut attrs, &input, "serde", false)?;
filter_parse_attrs(&mut attrs, &input, "openapi", true)?;

// parse #[doc] attributes
for attr in &input.attrs {
if attr.path.is_ident("doc") {
if attr.path().is_ident("doc") {
if let Some(lit) = parse_doc_attr(attr)? {
attrs.doc.push(lit.value());
}
Expand Down
32 changes: 18 additions & 14 deletions derive/src/parser.rs
Expand Up @@ -5,8 +5,8 @@ use crate::{
use proc_macro2::{Ident, Span};
use serde_derive_internals::attr::RenameRule;
use syn::{
punctuated::Punctuated, spanned::Spanned as _, AngleBracketedGenericArguments, DataEnum, DataStruct, DataUnion, Fields,
FieldsNamed, GenericArgument, LitStr, PathArguments, Type, TypePath
punctuated::Punctuated, spanned::Spanned as _, AngleBracketedGenericArguments, DataEnum, DataStruct, DataUnion, Field,
Fields, FieldsNamed, GenericArgument, LitStr, Meta, PathArguments, Type, TypePath
};
use syn_path::path;

Expand Down Expand Up @@ -43,21 +43,25 @@ pub(super) enum ParseDataType {
Unit
}

fn filter_parse_attrs(attrs: &mut FieldAttributes, input: &Field, filter: &str, error_on_unknown: bool) -> syn::Result<()> {
for attr in &input.attrs {
match &attr.meta {
Meta::List(meta) if meta.path.is_ident(filter) => {
attrs.parse_from(meta.tokens.clone(), error_on_unknown)?;
},
_ => {}
}
}
Ok(())
}

fn parse_named_fields(named_fields: &FieldsNamed, rename_all: Option<&LitStr>) -> syn::Result<Vec<ParseDataField>> {
let mut fields: Vec<ParseDataField> = Vec::new();
for f in &named_fields.named {
// parse #[serde] and #[openapi] attributes
let mut attrs = FieldAttributes::default();
for attr in &f.attrs {
if attr.path.is_ident("serde") {
attrs.parse_from(attr, false)?;
}
}
for attr in &f.attrs {
if attr.path.is_ident("openapi") {
attrs.parse_from(attr, true)?;
}
}
filter_parse_attrs(&mut attrs, f, "serde", false)?;
filter_parse_attrs(&mut attrs, f, "openapi", true)?;

// skip this field if desired
if attrs.skip_serializing && attrs.skip_deserializing {
Expand All @@ -67,7 +71,7 @@ fn parse_named_fields(named_fields: &FieldsNamed, rename_all: Option<&LitStr>) -
// parse #[doc] attributes
let mut doc = Vec::new();
for attr in &f.attrs {
if attr.path.is_ident("doc") {
if attr.path().is_ident("doc") {
if let Some(lit) = parse_doc_attr(attr)? {
doc.push(lit.value());
}
Expand Down Expand Up @@ -339,7 +343,7 @@ pub(super) fn parse_enum(ident: &Ident, inum: &DataEnum, attrs: &ContainerAttrib
},
// no variants
(None, None) => Err(syn::Error::new(
inum.brace_token.span,
inum.brace_token.span.span(),
"#[derive(OpenapiType)] does not support enums with no variants"
))
}
Expand Down
11 changes: 10 additions & 1 deletion derive/src/util.rs
@@ -1,5 +1,5 @@
use proc_macro2::{Ident, Span};
use syn::{Lit, LitStr};
use syn::{spanned::Spanned as _, Expr, Lit, LitStr};

/// Convert [Ident], [String] and [&str] into a [LitStr].
pub(super) trait ToLitStr {
Expand Down Expand Up @@ -37,3 +37,12 @@ impl ExpectLit for Lit {
}
}
}

impl ExpectLit for Expr {
fn expect_str(self) -> syn::Result<LitStr> {
match self {
Expr::Lit(lit) => lit.lit.expect_str(),
_ => Err(syn::Error::new(self.span(), "Expected string literal"))
}
}
}
4 changes: 1 addition & 3 deletions tests/fail/not_openapitype_generics.stderr
Expand Up @@ -15,11 +15,9 @@ error[E0599]: the function or associated item `schema` exists for struct `Foo<Ba
|
= note: trait bound `Bar: OpenapiType` was not satisfied
= note: the following trait bounds were not satisfied:
`Bar: OpenapiType`
which is required by `Foo<Bar>: OpenapiType`
`Foo<Bar>: OpenapiType`
which is required by `&Foo<Bar>: OpenapiType`
note: the following trait must be implemented
note: the trait `OpenapiType` must be implemented
--> src/lib.rs
|
| pub trait OpenapiType {
Expand Down

0 comments on commit 7d29efe

Please sign in to comment.