ruma-api-macros: Add more error spans for request block
This commit is contained in:
parent
eab2374253
commit
c277c0d257
@ -309,7 +309,7 @@ pub struct RawApi {
|
|||||||
/// The `metadata` section of the macro.
|
/// The `metadata` section of the macro.
|
||||||
pub metadata: RawMetadata,
|
pub metadata: RawMetadata,
|
||||||
/// The `request` section of the macro.
|
/// The `request` section of the macro.
|
||||||
pub request: Vec<Field>,
|
pub request: RawRequest,
|
||||||
/// The `response` section of the macro.
|
/// The `response` section of the macro.
|
||||||
pub response: Vec<Field>,
|
pub response: Vec<Field>,
|
||||||
}
|
}
|
||||||
@ -317,10 +317,7 @@ pub struct RawApi {
|
|||||||
impl Parse for RawApi {
|
impl Parse for RawApi {
|
||||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
let metadata = input.parse::<RawMetadata>()?;
|
let metadata = input.parse::<RawMetadata>()?;
|
||||||
|
let request = input.parse::<RawRequest>()?;
|
||||||
input.parse::<kw::request>()?;
|
|
||||||
let request;
|
|
||||||
braced!(request in input);
|
|
||||||
|
|
||||||
input.parse::<kw::response>()?;
|
input.parse::<kw::response>()?;
|
||||||
let response;
|
let response;
|
||||||
@ -328,10 +325,7 @@ impl Parse for RawApi {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
metadata,
|
metadata,
|
||||||
request: request
|
request,
|
||||||
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
response: response
|
response: response
|
||||||
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
|
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -360,3 +354,24 @@ impl Parse for RawMetadata {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RawRequest {
|
||||||
|
pub request_kw: kw::request,
|
||||||
|
pub fields: Vec<Field>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for RawRequest {
|
||||||
|
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||||
|
let request_kw = input.parse::<kw::request>()?;
|
||||||
|
let fields;
|
||||||
|
braced!(fields in input);
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
request_kw,
|
||||||
|
fields: fields
|
||||||
|
.parse_terminated::<Field, Token![,]>(Field::parse_named)?
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Details of the `request` section of the procedural macro.
|
//! Details of the `request` section of the procedural macro.
|
||||||
|
|
||||||
use std::convert::TryFrom;
|
use std::{convert::TryFrom, mem};
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{quote, quote_spanned, ToTokens};
|
use quote::{quote, quote_spanned, ToTokens};
|
||||||
@ -8,7 +8,7 @@ use syn::{spanned::Spanned, Field, Ident};
|
|||||||
|
|
||||||
use crate::api::{
|
use crate::api::{
|
||||||
attribute::{Meta, MetaNameValue},
|
attribute::{Meta, MetaNameValue},
|
||||||
strip_serde_attrs,
|
strip_serde_attrs, RawRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The result of processing the `request` section of the macro.
|
/// The result of processing the `request` section of the macro.
|
||||||
@ -121,66 +121,92 @@ impl Request {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Vec<Field>> for Request {
|
impl TryFrom<RawRequest> for Request {
|
||||||
type Error = syn::Error;
|
type Error = syn::Error;
|
||||||
|
|
||||||
fn try_from(fields: Vec<Field>) -> syn::Result<Self> {
|
fn try_from(raw: RawRequest) -> syn::Result<Self> {
|
||||||
let fields: Vec<_> = fields.into_iter().map(|mut field| {
|
let mut newtype_body_field = None;
|
||||||
let mut field_kind = None;
|
|
||||||
let mut header = None;
|
|
||||||
|
|
||||||
field.attrs.retain(|attr| {
|
let fields = raw
|
||||||
let meta = match Meta::from_attribute(attr) {
|
.fields
|
||||||
Some(m) => m,
|
.into_iter()
|
||||||
None => return true,
|
.map(|mut field| {
|
||||||
};
|
let mut field_kind = None;
|
||||||
|
let mut header = None;
|
||||||
|
|
||||||
match meta {
|
for attr in mem::replace(&mut field.attrs, Vec::new()) {
|
||||||
Meta::Word(ident) => {
|
let meta = match Meta::from_attribute(&attr) {
|
||||||
assert!(
|
Some(m) => m,
|
||||||
field_kind.is_none(),
|
None => {
|
||||||
"ruma_api! field kind can only be set once per field"
|
field.attrs.push(attr);
|
||||||
);
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
field_kind = Some(match &ident.to_string()[..] {
|
if field_kind.is_some() {
|
||||||
"body" => RequestFieldKind::NewtypeBody,
|
return Err(syn::Error::new_spanned(
|
||||||
"path" => RequestFieldKind::Path,
|
attr,
|
||||||
"query" => RequestFieldKind::Query,
|
"There can only be one field kind attribute",
|
||||||
_ => panic!("ruma_api! single-word attribute on requests must be: body, path, or query"),
|
));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
Meta::NameValue(MetaNameValue { name, value }) => {
|
|
||||||
assert!(
|
|
||||||
name == "header",
|
|
||||||
"ruma_api! name/value pair attribute on requests must be: header"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
field_kind.is_none(),
|
|
||||||
"ruma_api! field kind can only be set once per field"
|
|
||||||
);
|
|
||||||
|
|
||||||
header = Some(value);
|
field_kind = Some(match meta {
|
||||||
field_kind = Some(RequestFieldKind::Header);
|
Meta::Word(ident) => {
|
||||||
}
|
match &ident.to_string()[..] {
|
||||||
|
"body" => {
|
||||||
|
if let Some(f) = &newtype_body_field {
|
||||||
|
let mut error = syn::Error::new_spanned(
|
||||||
|
field,
|
||||||
|
"There can only be one newtype body field",
|
||||||
|
);
|
||||||
|
error.combine(syn::Error::new_spanned(
|
||||||
|
f,
|
||||||
|
"Previous newtype body field",
|
||||||
|
));
|
||||||
|
return Err(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype_body_field = Some(field.clone());
|
||||||
|
RequestFieldKind::NewtypeBody
|
||||||
|
}
|
||||||
|
"path" => RequestFieldKind::Path,
|
||||||
|
"query" => RequestFieldKind::Query,
|
||||||
|
_ => {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
ident,
|
||||||
|
"Invalid #[ruma_api] argument, expected one of `body`, `path`, `query`",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Meta::NameValue(MetaNameValue { name, value }) => {
|
||||||
|
if name != "header" {
|
||||||
|
return Err(syn::Error::new_spanned(
|
||||||
|
name,
|
||||||
|
"Invalid #[ruma_api] argument with value, expected `header`"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
header = Some(value);
|
||||||
|
RequestFieldKind::Header
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Ok(RequestField::new(
|
||||||
});
|
field_kind.unwrap_or(RequestFieldKind::Body),
|
||||||
|
field,
|
||||||
|
header,
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.collect::<syn::Result<Vec<_>>>()?;
|
||||||
|
|
||||||
RequestField::new(field_kind.unwrap_or(RequestFieldKind::Body), field, header)
|
if newtype_body_field.is_some() && fields.iter().any(|f| f.is_body()) {
|
||||||
}).collect();
|
return Err(syn::Error::new_spanned(
|
||||||
|
// TODO: raw,
|
||||||
let num_body_fields = fields.iter().filter(|f| f.is_body()).count();
|
raw.request_kw,
|
||||||
let num_newtype_body_fields = fields.iter().filter(|f| f.is_newtype_body()).count();
|
"Can't have both a newtype body field and regular body fields",
|
||||||
assert!(
|
));
|
||||||
num_newtype_body_fields <= 1,
|
|
||||||
"ruma_api! request can only have one newtype body field"
|
|
||||||
);
|
|
||||||
if num_newtype_body_fields == 1 {
|
|
||||||
assert!(
|
|
||||||
num_body_fields == 0,
|
|
||||||
"ruma_api! request can't have both regular body fields and a newtype body field"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Self { fields })
|
Ok(Self { fields })
|
||||||
@ -329,11 +355,6 @@ impl RequestField {
|
|||||||
self.kind() == RequestFieldKind::Header
|
self.kind() == RequestFieldKind::Header
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether or not this request field is a newtype body kind.
|
|
||||||
fn is_newtype_body(&self) -> bool {
|
|
||||||
self.kind() == RequestFieldKind::NewtypeBody
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether or not this request field is a path kind.
|
/// Whether or not this request field is a path kind.
|
||||||
fn is_path(&self) -> bool {
|
fn is_path(&self) -> bool {
|
||||||
self.kind() == RequestFieldKind::Path
|
self.kind() == RequestFieldKind::Path
|
||||||
|
Loading…
x
Reference in New Issue
Block a user