api-macros: Refactor parsing logic

This commit is contained in:
Jonas Platte 2021-04-05 12:39:22 +02:00
parent e8e0ceb17d
commit 2e0f787ccd
No known key found for this signature in database
GPG Key ID: CC154DE0E30B7C67

View File

@ -4,9 +4,8 @@ use syn::{
braced, braced,
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
spanned::Spanned, spanned::Spanned,
AngleBracketedGenericArguments, Attribute, BoundLifetimes, Field, GenericArgument, Ident, visit::Visit,
Lifetime, LifetimeDef, ParenthesizedGenericArguments, PathArguments, Token, Type, TypeArray, Attribute, Field, Ident, Lifetime, Token, Type,
TypeBareFn, TypeGroup, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, TypeTuple,
}; };
use super::{ use super::{
@ -18,9 +17,11 @@ use super::{
use crate::util; use crate::util;
mod kw { mod kw {
syn::custom_keyword!(error); use syn::custom_keyword;
syn::custom_keyword!(request);
syn::custom_keyword!(response); custom_keyword!(error);
custom_keyword!(request);
custom_keyword!(response);
} }
impl Parse for Api { impl Parse for Api {
@ -74,13 +75,12 @@ impl Parse for Request {
let fields; let fields;
braced!(fields in input); braced!(fields in input);
let fields = fields.parse_terminated::<Field, Token![,]>(Field::parse_named)?;
let mut newtype_body_field = None; let mut newtype_body_field = None;
let mut query_map_field = None; let mut query_map_field = None;
let mut lifetimes = RequestLifetimes::default(); let mut lifetimes = RequestLifetimes::default();
let fields = fields let fields: Vec<_> = fields
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
.into_iter() .into_iter()
.map(|mut field| { .map(|mut field| {
let mut field_kind = None; let mut field_kind = None;
@ -145,31 +145,31 @@ impl Parse for Request {
match field_kind.unwrap_or(RequestFieldKind::Body) { match field_kind.unwrap_or(RequestFieldKind::Body) {
RequestFieldKind::Header => { RequestFieldKind::Header => {
collect_lifetime_ident(&mut lifetimes.header, &field.ty) collect_lifetime_idents(&mut lifetimes.header, &field.ty)
} }
RequestFieldKind::Body => { RequestFieldKind::Body => {
collect_lifetime_ident(&mut lifetimes.body, &field.ty) collect_lifetime_idents(&mut lifetimes.body, &field.ty)
} }
RequestFieldKind::NewtypeBody => { RequestFieldKind::NewtypeBody => {
collect_lifetime_ident(&mut lifetimes.body, &field.ty) collect_lifetime_idents(&mut lifetimes.body, &field.ty)
} }
RequestFieldKind::NewtypeRawBody => { RequestFieldKind::NewtypeRawBody => {
collect_lifetime_ident(&mut lifetimes.body, &field.ty) collect_lifetime_idents(&mut lifetimes.body, &field.ty)
} }
RequestFieldKind::Path => { RequestFieldKind::Path => {
collect_lifetime_ident(&mut lifetimes.path, &field.ty) collect_lifetime_idents(&mut lifetimes.path, &field.ty)
} }
RequestFieldKind::Query => { RequestFieldKind::Query => {
collect_lifetime_ident(&mut lifetimes.query, &field.ty) collect_lifetime_idents(&mut lifetimes.query, &field.ty)
} }
RequestFieldKind::QueryMap => { RequestFieldKind::QueryMap => {
collect_lifetime_ident(&mut lifetimes.query, &field.ty) collect_lifetime_idents(&mut lifetimes.query, &field.ty)
} }
} }
Ok(RequestField::new(field_kind.unwrap_or(RequestFieldKind::Body), field, header)) Ok(RequestField::new(field_kind.unwrap_or(RequestFieldKind::Body), field, header))
}) })
.collect::<syn::Result<Vec<_>>>()?; .collect::<syn::Result<_>>()?;
if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) { if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) {
// TODO: highlight conflicting fields, // TODO: highlight conflicting fields,
@ -207,26 +207,19 @@ impl Parse for Response {
let fields; let fields;
braced!(fields in input); braced!(fields in input);
let fields = fields
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
.into_iter()
.map(|f| {
if has_lifetime(&f.ty) {
Err(syn::Error::new(
f.ident.span(),
"Lifetimes on Response fields cannot be supported until GAT are stable",
))
} else {
Ok(f)
}
})
.collect::<Result<Vec<_>, _>>()?;
let mut newtype_body_field = None; let mut newtype_body_field = None;
let fields = fields let fields: Vec<_> = fields
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
.into_iter() .into_iter()
.map(|mut field| { .map(|mut field| {
if has_lifetime(&field.ty) {
return Err(syn::Error::new(
field.ident.span(),
"Lifetimes on Response fields cannot be supported until GAT are stable",
));
}
let mut field_kind = None; let mut field_kind = None;
let mut header = None; let mut header = None;
@ -277,7 +270,7 @@ impl Parse for Response {
ResponseFieldKind::NewtypeRawBody => ResponseField::NewtypeRawBody(field), ResponseFieldKind::NewtypeRawBody => ResponseField::NewtypeRawBody(field),
}) })
}) })
.collect::<syn::Result<Vec<_>>>()?; .collect::<syn::Result<_>>()?;
if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) { if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) {
// TODO: highlight conflicting fields, // TODO: highlight conflicting fields,
@ -292,118 +285,20 @@ impl Parse for Response {
} }
fn has_lifetime(ty: &Type) -> bool { fn has_lifetime(ty: &Type) -> bool {
match ty { let mut lifetimes = BTreeSet::new();
Type::Path(TypePath { path, .. }) => { collect_lifetime_idents(&mut lifetimes, ty);
let mut found = false; !lifetimes.is_empty()
for seg in &path.segments {
match &seg.arguments {
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args, ..
}) => {
for gen in args {
if let GenericArgument::Type(ty) = gen {
if has_lifetime(&ty) {
found = true;
};
} else if let GenericArgument::Lifetime(_) = gen {
return true;
} }
}
} fn collect_lifetime_idents(lifetimes: &mut BTreeSet<Lifetime>, ty: &Type) {
PathArguments::Parenthesized(ParenthesizedGenericArguments { struct Visitor<'lt>(&'lt mut BTreeSet<Lifetime>);
inputs, .. impl<'ast> Visit<'ast> for Visitor<'_> {
}) => { fn visit_lifetime(&mut self, lt: &'ast Lifetime) {
for ty in inputs { self.0.insert(lt.clone());
if has_lifetime(ty) {
found = true;
}
}
}
_ => {}
}
}
found
}
Type::Reference(TypeReference { elem, lifetime, .. }) => {
if lifetime.is_some() {
true
} else {
has_lifetime(&elem)
}
}
Type::Tuple(TypeTuple { elems, .. }) => {
let mut found = false;
for ty in elems {
if has_lifetime(ty) {
found = true;
}
}
found
}
Type::Paren(TypeParen { elem, .. }) => has_lifetime(&elem),
Type::Group(TypeGroup { elem, .. }) => has_lifetime(&*elem),
Type::Ptr(TypePtr { elem, .. }) => has_lifetime(&*elem),
Type::Slice(TypeSlice { elem, .. }) => has_lifetime(&*elem),
Type::Array(TypeArray { elem, .. }) => has_lifetime(&*elem),
Type::BareFn(TypeBareFn { lifetimes: Some(BoundLifetimes { .. }), .. }) => true,
_ => false,
} }
} }
fn collect_lifetime_ident(lifetimes: &mut BTreeSet<Lifetime>, ty: &Type) { Visitor(lifetimes).visit_type(ty)
match ty {
Type::Path(TypePath { path, .. }) => {
for seg in &path.segments {
match &seg.arguments {
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
args, ..
}) => {
for gen in args {
if let GenericArgument::Type(ty) = gen {
collect_lifetime_ident(lifetimes, &ty);
} else if let GenericArgument::Lifetime(lt) = gen {
lifetimes.insert(lt.clone());
}
}
}
PathArguments::Parenthesized(ParenthesizedGenericArguments {
inputs, ..
}) => {
for ty in inputs {
collect_lifetime_ident(lifetimes, ty);
}
}
_ => {}
}
}
}
Type::Reference(TypeReference { elem, lifetime, .. }) => {
collect_lifetime_ident(lifetimes, &*elem);
if let Some(lt) = lifetime {
lifetimes.insert(lt.clone());
}
}
Type::Tuple(TypeTuple { elems, .. }) => {
for ty in elems {
collect_lifetime_ident(lifetimes, ty);
}
}
Type::Paren(TypeParen { elem, .. }) => collect_lifetime_ident(lifetimes, &*elem),
Type::Group(TypeGroup { elem, .. }) => collect_lifetime_ident(lifetimes, &*elem),
Type::Ptr(TypePtr { elem, .. }) => collect_lifetime_ident(lifetimes, &*elem),
Type::Slice(TypeSlice { elem, .. }) => collect_lifetime_ident(lifetimes, &*elem),
Type::Array(TypeArray { elem, .. }) => collect_lifetime_ident(lifetimes, &*elem),
Type::BareFn(TypeBareFn {
lifetimes: Some(BoundLifetimes { lifetimes: fn_lifetimes, .. }),
..
}) => {
for lt in fn_lifetimes {
let LifetimeDef { lifetime, .. } = lt;
lifetimes.insert(lifetime.clone());
}
}
_ => {}
}
} }
fn req_res_meta_word<T>( fn req_res_meta_word<T>(