api-macros: Refactor parsing logic
This commit is contained in:
parent
e8e0ceb17d
commit
2e0f787ccd
@ -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>(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user