mirror of
https://gitlab.com/msrd0/gotham-restful.git
synced 2025-04-20 23:07:01 +00:00
update to gotham 0.5 and start using rustfmt
This commit is contained in:
parent
5317e50961
commit
d55b0897e9
39 changed files with 1798 additions and 2095 deletions
|
@ -1,73 +1,65 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use std::cmp::min;
|
||||
use syn::{
|
||||
spanned::Spanned,
|
||||
Data,
|
||||
DeriveInput,
|
||||
Error,
|
||||
Field,
|
||||
Fields,
|
||||
Ident,
|
||||
Result,
|
||||
Type
|
||||
};
|
||||
use syn::{spanned::Spanned, Data, DeriveInput, Error, Field, Fields, Ident, Result, Type};
|
||||
|
||||
struct ParsedFields
|
||||
{
|
||||
fields : Vec<(Ident, Type)>,
|
||||
named : bool
|
||||
struct ParsedFields {
|
||||
fields: Vec<(Ident, Type)>,
|
||||
named: bool
|
||||
}
|
||||
|
||||
impl ParsedFields
|
||||
{
|
||||
fn from_named<I>(fields : I) -> Self
|
||||
impl ParsedFields {
|
||||
fn from_named<I>(fields: I) -> Self
|
||||
where
|
||||
I : Iterator<Item = Field>
|
||||
I: Iterator<Item = Field>
|
||||
{
|
||||
let fields = fields.map(|field| (field.ident.unwrap(), field.ty)).collect();
|
||||
Self { fields, named: true }
|
||||
}
|
||||
|
||||
fn from_unnamed<I>(fields : I) -> Self
|
||||
|
||||
fn from_unnamed<I>(fields: I) -> Self
|
||||
where
|
||||
I : Iterator<Item = Field>
|
||||
I: Iterator<Item = Field>
|
||||
{
|
||||
let fields = fields.enumerate().map(|(i, field)| (format_ident!("arg{}", i), field.ty)).collect();
|
||||
let fields = fields
|
||||
.enumerate()
|
||||
.map(|(i, field)| (format_ident!("arg{}", i), field.ty))
|
||||
.collect();
|
||||
Self { fields, named: false }
|
||||
}
|
||||
|
||||
fn from_unit() -> Self
|
||||
{
|
||||
Self { fields: Vec::new(), named: false }
|
||||
|
||||
fn from_unit() -> Self {
|
||||
Self {
|
||||
fields: Vec::new(),
|
||||
named: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_from_body(input : DeriveInput) -> Result<TokenStream>
|
||||
{
|
||||
pub fn expand_from_body(input: DeriveInput) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
let ident = input.ident;
|
||||
let generics = input.generics;
|
||||
|
||||
|
||||
let strukt = match input.data {
|
||||
Data::Enum(inum) => Err(inum.enum_token.span()),
|
||||
Data::Struct(strukt) => Ok(strukt),
|
||||
Data::Union(uni) => Err(uni.union_token.span())
|
||||
}.map_err(|span| Error::new(span, "#[derive(FromBody)] only works for structs"))?;
|
||||
|
||||
}
|
||||
.map_err(|span| Error::new(span, "#[derive(FromBody)] only works for structs"))?;
|
||||
|
||||
let fields = match strukt.fields {
|
||||
Fields::Named(named) => ParsedFields::from_named(named.named.into_iter()),
|
||||
Fields::Unnamed(unnamed) => ParsedFields::from_unnamed(unnamed.unnamed.into_iter()),
|
||||
Fields::Unit => ParsedFields::from_unit()
|
||||
};
|
||||
|
||||
|
||||
let mut where_clause = quote!();
|
||||
let mut block = quote!();
|
||||
let mut body_ident = format_ident!("_body");
|
||||
let mut type_ident = format_ident!("_type");
|
||||
|
||||
if let Some(body_field) = fields.fields.get(0)
|
||||
{
|
||||
|
||||
if let Some(body_field) = fields.fields.get(0) {
|
||||
body_ident = body_field.0.clone();
|
||||
let body_ty = &body_field.1;
|
||||
where_clause = quote! {
|
||||
|
@ -80,9 +72,8 @@ pub fn expand_from_body(input : DeriveInput) -> Result<TokenStream>
|
|||
let #body_ident : #body_ty = #body_ident.into();
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(type_field) = fields.fields.get(1)
|
||||
{
|
||||
|
||||
if let Some(type_field) = fields.fields.get(1) {
|
||||
type_ident = type_field.0.clone();
|
||||
let type_ty = &type_field.1;
|
||||
where_clause = quote! {
|
||||
|
@ -94,9 +85,8 @@ pub fn expand_from_body(input : DeriveInput) -> Result<TokenStream>
|
|||
let #type_ident : #type_ty = #type_ident.into();
|
||||
};
|
||||
}
|
||||
|
||||
for field in &fields.fields[min(2, fields.fields.len())..]
|
||||
{
|
||||
|
||||
for field in &fields.fields[min(2, fields.fields.len())..] {
|
||||
let field_ident = &field.0;
|
||||
let field_ty = &field.1;
|
||||
where_clause = quote! {
|
||||
|
@ -108,20 +98,20 @@ pub fn expand_from_body(input : DeriveInput) -> Result<TokenStream>
|
|||
let #field_ident : #field_ty = Default::default();
|
||||
};
|
||||
}
|
||||
|
||||
let field_names : Vec<&Ident> = fields.fields.iter().map(|field| &field.0).collect();
|
||||
|
||||
let field_names: Vec<&Ident> = fields.fields.iter().map(|field| &field.0).collect();
|
||||
let ctor = if fields.named {
|
||||
quote!(Self { #(#field_names),* })
|
||||
} else {
|
||||
quote!(Self ( #(#field_names),* ))
|
||||
};
|
||||
|
||||
|
||||
Ok(quote! {
|
||||
impl #generics #krate::FromBody for #ident #generics
|
||||
where #where_clause
|
||||
{
|
||||
type Err = ::std::convert::Infallible;
|
||||
|
||||
|
||||
fn from_body(#body_ident : #krate::gotham::hyper::body::Bytes, #type_ident : #krate::Mime) -> Result<Self, ::std::convert::Infallible>
|
||||
{
|
||||
#block
|
||||
|
|
|
@ -21,114 +21,96 @@ mod openapi_type;
|
|||
use openapi_type::expand_openapi_type;
|
||||
|
||||
#[inline]
|
||||
fn print_tokens(tokens : TokenStream2) -> TokenStream
|
||||
{
|
||||
fn print_tokens(tokens: TokenStream2) -> TokenStream {
|
||||
//eprintln!("{}", tokens);
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn expand_derive<F>(input : TokenStream, expand : F) -> TokenStream
|
||||
fn expand_derive<F>(input: TokenStream, expand: F) -> TokenStream
|
||||
where
|
||||
F : FnOnce(DeriveInput) -> Result<TokenStream2>
|
||||
F: FnOnce(DeriveInput) -> Result<TokenStream2>
|
||||
{
|
||||
print_tokens(expand(parse_macro_input!(input))
|
||||
.unwrap_or_else(|err| err.to_compile_error()))
|
||||
print_tokens(expand(parse_macro_input!(input)).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn expand_macro<F, A, I>(attrs : TokenStream, item : TokenStream, expand : F) -> TokenStream
|
||||
fn expand_macro<F, A, I>(attrs: TokenStream, item: TokenStream, expand: F) -> TokenStream
|
||||
where
|
||||
F : FnOnce(A, I) -> Result<TokenStream2>,
|
||||
A : ParseMacroInput,
|
||||
I : ParseMacroInput
|
||||
F: FnOnce(A, I) -> Result<TokenStream2>,
|
||||
A: ParseMacroInput,
|
||||
I: ParseMacroInput
|
||||
{
|
||||
print_tokens(expand(parse_macro_input!(attrs), parse_macro_input!(item))
|
||||
.unwrap_or_else(|err| err.to_compile_error()))
|
||||
print_tokens(expand(parse_macro_input!(attrs), parse_macro_input!(item)).unwrap_or_else(|err| err.to_compile_error()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn krate() -> TokenStream2
|
||||
{
|
||||
fn krate() -> TokenStream2 {
|
||||
quote!(::gotham_restful)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(FromBody)]
|
||||
pub fn derive_from_body(input : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn derive_from_body(input: TokenStream) -> TokenStream {
|
||||
expand_derive(input, expand_from_body)
|
||||
}
|
||||
|
||||
#[cfg(feature = "openapi")]
|
||||
#[proc_macro_derive(OpenapiType, attributes(openapi))]
|
||||
pub fn derive_openapi_type(input : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn derive_openapi_type(input: TokenStream) -> TokenStream {
|
||||
expand_derive(input, expand_openapi_type)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(RequestBody, attributes(supported_types))]
|
||||
pub fn derive_request_body(input : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn derive_request_body(input: TokenStream) -> TokenStream {
|
||||
expand_derive(input, expand_request_body)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(Resource, attributes(resource))]
|
||||
pub fn derive_resource(input : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn derive_resource(input: TokenStream) -> TokenStream {
|
||||
expand_derive(input, expand_resource)
|
||||
}
|
||||
|
||||
#[proc_macro_derive(ResourceError, attributes(display, from, status))]
|
||||
pub fn derive_resource_error(input : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn derive_resource_error(input: TokenStream) -> TokenStream {
|
||||
expand_derive(input, expand_resource_error)
|
||||
}
|
||||
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn read_all(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn read_all(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::ReadAll, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn read(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn read(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::Read, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn search(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn search(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::Search, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn create(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn create(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::Create, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn change_all(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn change_all(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::ChangeAll, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn change(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn change(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::Change, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn remove_all(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn remove_all(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::RemoveAll, attr, item))
|
||||
}
|
||||
|
||||
#[proc_macro_attribute]
|
||||
pub fn remove(attr : TokenStream, item : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn remove(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
expand_macro(attr, item, |attr, item| expand_method(Method::Remove, attr, item))
|
||||
}
|
||||
|
|
|
@ -2,26 +2,13 @@ use crate::util::CollectToResult;
|
|||
use heck::{CamelCase, SnakeCase};
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{
|
||||
spanned::Spanned,
|
||||
Attribute,
|
||||
AttributeArgs,
|
||||
Error,
|
||||
FnArg,
|
||||
ItemFn,
|
||||
Lit,
|
||||
LitBool,
|
||||
Meta,
|
||||
NestedMeta,
|
||||
PatType,
|
||||
Result,
|
||||
ReturnType,
|
||||
Type
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use syn::{
|
||||
spanned::Spanned, Attribute, AttributeArgs, Error, FnArg, ItemFn, Lit, LitBool, Meta, NestedMeta, PatType, Result,
|
||||
ReturnType, Type
|
||||
};
|
||||
|
||||
pub enum Method
|
||||
{
|
||||
pub enum Method {
|
||||
ReadAll,
|
||||
Read,
|
||||
Search,
|
||||
|
@ -32,12 +19,10 @@ pub enum Method
|
|||
Remove
|
||||
}
|
||||
|
||||
impl FromStr for Method
|
||||
{
|
||||
impl FromStr for Method {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(str : &str) -> Result<Self>
|
||||
{
|
||||
|
||||
fn from_str(str: &str) -> Result<Self> {
|
||||
match str {
|
||||
"ReadAll" | "read_all" => Ok(Self::ReadAll),
|
||||
"Read" | "read" => Ok(Self::Read),
|
||||
|
@ -52,12 +37,10 @@ impl FromStr for Method
|
|||
}
|
||||
}
|
||||
|
||||
impl Method
|
||||
{
|
||||
pub fn type_names(&self) -> Vec<&'static str>
|
||||
{
|
||||
impl Method {
|
||||
pub fn type_names(&self) -> Vec<&'static str> {
|
||||
use Method::*;
|
||||
|
||||
|
||||
match self {
|
||||
ReadAll | RemoveAll => vec![],
|
||||
Read | Remove => vec!["ID"],
|
||||
|
@ -66,11 +49,10 @@ impl Method
|
|||
Change => vec!["ID", "Body"]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trait_ident(&self) -> Ident
|
||||
{
|
||||
|
||||
pub fn trait_ident(&self) -> Ident {
|
||||
use Method::*;
|
||||
|
||||
|
||||
let name = match self {
|
||||
ReadAll => "ReadAll",
|
||||
Read => "Read",
|
||||
|
@ -83,11 +65,10 @@ impl Method
|
|||
};
|
||||
format_ident!("Resource{}", name)
|
||||
}
|
||||
|
||||
pub fn fn_ident(&self) -> Ident
|
||||
{
|
||||
|
||||
pub fn fn_ident(&self) -> Ident {
|
||||
use Method::*;
|
||||
|
||||
|
||||
let name = match self {
|
||||
ReadAll => "read_all",
|
||||
Read => "read",
|
||||
|
@ -100,26 +81,26 @@ impl Method
|
|||
};
|
||||
format_ident!("{}", name)
|
||||
}
|
||||
|
||||
pub fn mod_ident(&self, resource : &str) -> Ident
|
||||
{
|
||||
format_ident!("_gotham_restful_resource_{}_method_{}", resource.to_snake_case(), self.fn_ident())
|
||||
|
||||
pub fn mod_ident(&self, resource: &str) -> Ident {
|
||||
format_ident!(
|
||||
"_gotham_restful_resource_{}_method_{}",
|
||||
resource.to_snake_case(),
|
||||
self.fn_ident()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handler_struct_ident(&self, resource : &str) -> Ident
|
||||
{
|
||||
|
||||
pub fn handler_struct_ident(&self, resource: &str) -> Ident {
|
||||
format_ident!("{}{}Handler", resource.to_camel_case(), self.trait_ident())
|
||||
}
|
||||
|
||||
pub fn setup_ident(&self, resource : &str) -> Ident
|
||||
{
|
||||
|
||||
pub fn setup_ident(&self, resource: &str) -> Ident {
|
||||
format_ident!("{}_{}_setup_impl", resource.to_snake_case(), self.fn_ident())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum MethodArgumentType
|
||||
{
|
||||
enum MethodArgumentType {
|
||||
StateRef,
|
||||
StateMutRef,
|
||||
MethodArg(Type),
|
||||
|
@ -128,116 +109,111 @@ enum MethodArgumentType
|
|||
AuthStatusRef(Type)
|
||||
}
|
||||
|
||||
impl MethodArgumentType
|
||||
{
|
||||
fn is_state_ref(&self) -> bool
|
||||
{
|
||||
impl MethodArgumentType {
|
||||
fn is_state_ref(&self) -> bool {
|
||||
matches!(self, Self::StateRef | Self::StateMutRef)
|
||||
}
|
||||
|
||||
fn is_method_arg(&self) -> bool
|
||||
{
|
||||
|
||||
fn is_method_arg(&self) -> bool {
|
||||
matches!(self, Self::MethodArg(_))
|
||||
}
|
||||
|
||||
fn is_database_conn(&self) -> bool
|
||||
{
|
||||
|
||||
fn is_database_conn(&self) -> bool {
|
||||
matches!(self, Self::DatabaseConnection(_))
|
||||
}
|
||||
|
||||
fn is_auth_status(&self) -> bool
|
||||
{
|
||||
|
||||
fn is_auth_status(&self) -> bool {
|
||||
matches!(self, Self::AuthStatus(_) | Self::AuthStatusRef(_))
|
||||
}
|
||||
|
||||
fn ty(&self) -> Option<&Type>
|
||||
{
|
||||
|
||||
fn ty(&self) -> Option<&Type> {
|
||||
match self {
|
||||
Self::MethodArg(ty) | Self::DatabaseConnection(ty) | Self::AuthStatus(ty) | Self::AuthStatusRef(ty) => Some(ty),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
fn quote_ty(&self) -> Option<TokenStream>
|
||||
{
|
||||
|
||||
fn quote_ty(&self) -> Option<TokenStream> {
|
||||
self.ty().map(|ty| quote!(#ty))
|
||||
}
|
||||
}
|
||||
|
||||
struct MethodArgument
|
||||
{
|
||||
ident : Ident,
|
||||
ident_span : Span,
|
||||
ty : MethodArgumentType
|
||||
struct MethodArgument {
|
||||
ident: Ident,
|
||||
ident_span: Span,
|
||||
ty: MethodArgumentType
|
||||
}
|
||||
|
||||
impl Spanned for MethodArgument
|
||||
{
|
||||
fn span(&self) -> Span
|
||||
{
|
||||
impl Spanned for MethodArgument {
|
||||
fn span(&self) -> Span {
|
||||
self.ident_span
|
||||
}
|
||||
}
|
||||
|
||||
fn interpret_arg_ty(attrs : &[Attribute], name : &str, ty : Type) -> Result<MethodArgumentType>
|
||||
{
|
||||
let attr = attrs.iter()
|
||||
fn interpret_arg_ty(attrs: &[Attribute], name: &str, ty: Type) -> Result<MethodArgumentType> {
|
||||
let attr = attrs
|
||||
.iter()
|
||||
.find(|arg| arg.path.segments.iter().any(|path| &path.ident.to_string() == "rest_arg"))
|
||||
.map(|arg| arg.tokens.to_string());
|
||||
|
||||
|
||||
// TODO issue a warning for _state usage once diagnostics become stable
|
||||
if attr.as_deref() == Some("state") || (attr.is_none() && (name == "state" || name == "_state"))
|
||||
{
|
||||
if attr.as_deref() == Some("state") || (attr.is_none() && (name == "state" || name == "_state")) {
|
||||
return match ty {
|
||||
Type::Reference(ty) => Ok(if ty.mutability.is_none() { MethodArgumentType::StateRef } else { MethodArgumentType::StateMutRef }),
|
||||
_ => Err(Error::new(ty.span(), "The state parameter has to be a (mutable) reference to gotham_restful::State"))
|
||||
Type::Reference(ty) => Ok(if ty.mutability.is_none() {
|
||||
MethodArgumentType::StateRef
|
||||
} else {
|
||||
MethodArgumentType::StateMutRef
|
||||
}),
|
||||
_ => Err(Error::new(
|
||||
ty.span(),
|
||||
"The state parameter has to be a (mutable) reference to gotham_restful::State"
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
if cfg!(feature = "auth") && (attr.as_deref() == Some("auth") || (attr.is_none() && name == "auth"))
|
||||
{
|
||||
|
||||
if cfg!(feature = "auth") && (attr.as_deref() == Some("auth") || (attr.is_none() && name == "auth")) {
|
||||
return Ok(match ty {
|
||||
Type::Reference(ty) => MethodArgumentType::AuthStatusRef(*ty.elem),
|
||||
ty => MethodArgumentType::AuthStatus(ty)
|
||||
});
|
||||
}
|
||||
|
||||
if cfg!(feature = "database") && (attr.as_deref() == Some("connection") || attr.as_deref() == Some("conn") || (attr.is_none() && name == "conn"))
|
||||
|
||||
if cfg!(feature = "database")
|
||||
&& (attr.as_deref() == Some("connection") || attr.as_deref() == Some("conn") || (attr.is_none() && name == "conn"))
|
||||
{
|
||||
return Ok(MethodArgumentType::DatabaseConnection(match ty {
|
||||
Type::Reference(ty) => *ty.elem,
|
||||
ty => ty
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
Ok(MethodArgumentType::MethodArg(ty))
|
||||
}
|
||||
|
||||
fn interpret_arg(index : usize, arg : &PatType) -> Result<MethodArgument>
|
||||
{
|
||||
fn interpret_arg(index: usize, arg: &PatType) -> Result<MethodArgument> {
|
||||
let pat = &arg.pat;
|
||||
let ident = format_ident!("arg{}", index);
|
||||
let orig_name = quote!(#pat);
|
||||
let ty = interpret_arg_ty(&arg.attrs, &orig_name.to_string(), *arg.ty.clone())?;
|
||||
|
||||
Ok(MethodArgument { ident, ident_span: arg.pat.span(), ty })
|
||||
|
||||
Ok(MethodArgument {
|
||||
ident,
|
||||
ident_span: arg.pat.span(),
|
||||
ty
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "openapi")]
|
||||
fn expand_operation_id(attrs : &[NestedMeta]) -> TokenStream
|
||||
{
|
||||
let mut operation_id : Option<&Lit> = None;
|
||||
for meta in attrs
|
||||
{
|
||||
if let NestedMeta::Meta(Meta::NameValue(kv)) = meta
|
||||
{
|
||||
if kv.path.segments.last().map(|p| p.ident.to_string()) == Some("operation_id".to_owned())
|
||||
{
|
||||
fn expand_operation_id(attrs: &[NestedMeta]) -> TokenStream {
|
||||
let mut operation_id: Option<&Lit> = None;
|
||||
for meta in attrs {
|
||||
if let NestedMeta::Meta(Meta::NameValue(kv)) = meta {
|
||||
if kv.path.segments.last().map(|p| p.ident.to_string()) == Some("operation_id".to_owned()) {
|
||||
operation_id = Some(&kv.lit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
match operation_id {
|
||||
Some(operation_id) => quote! {
|
||||
fn operation_id() -> Option<String>
|
||||
|
@ -250,26 +226,24 @@ fn expand_operation_id(attrs : &[NestedMeta]) -> TokenStream
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "openapi"))]
|
||||
fn expand_operation_id(_ : &[NestedMeta]) -> TokenStream
|
||||
{
|
||||
fn expand_operation_id(_: &[NestedMeta]) -> TokenStream {
|
||||
quote!()
|
||||
}
|
||||
|
||||
fn expand_wants_auth(attrs : &[NestedMeta], default : bool) -> TokenStream
|
||||
{
|
||||
let default_lit = Lit::Bool(LitBool { value: default, span: Span::call_site() });
|
||||
fn expand_wants_auth(attrs: &[NestedMeta], default: bool) -> TokenStream {
|
||||
let default_lit = Lit::Bool(LitBool {
|
||||
value: default,
|
||||
span: Span::call_site()
|
||||
});
|
||||
let mut wants_auth = &default_lit;
|
||||
for meta in attrs
|
||||
{
|
||||
if let NestedMeta::Meta(Meta::NameValue(kv)) = meta
|
||||
{
|
||||
if kv.path.segments.last().map(|p| p.ident.to_string()) == Some("wants_auth".to_owned())
|
||||
{
|
||||
for meta in attrs {
|
||||
if let NestedMeta::Meta(Meta::NameValue(kv)) = meta {
|
||||
if kv.path.segments.last().map(|p| p.ident.to_string()) == Some("wants_auth".to_owned()) {
|
||||
wants_auth = &kv.lit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
quote! {
|
||||
fn wants_auth() -> bool
|
||||
{
|
||||
|
@ -279,73 +253,80 @@ fn expand_wants_auth(attrs : &[NestedMeta], default : bool) -> TokenStream
|
|||
}
|
||||
|
||||
#[allow(clippy::comparison_chain)]
|
||||
pub fn expand_method(method : Method, mut attrs : AttributeArgs, fun : ItemFn) -> Result<TokenStream>
|
||||
{
|
||||
pub fn expand_method(method: Method, mut attrs: AttributeArgs, fun: ItemFn) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
|
||||
|
||||
// parse attributes
|
||||
if attrs.len() < 1
|
||||
{
|
||||
return Err(Error::new(Span::call_site(), "Missing Resource struct. Example: #[read_all(MyResource)]"));
|
||||
if attrs.len() < 1 {
|
||||
return Err(Error::new(
|
||||
Span::call_site(),
|
||||
"Missing Resource struct. Example: #[read_all(MyResource)]"
|
||||
));
|
||||
}
|
||||
let resource_path = match attrs.remove(0) {
|
||||
NestedMeta::Meta(Meta::Path(path)) => path,
|
||||
p => return Err(Error::new(p.span(), "Expected name of the Resource struct this method belongs to"))
|
||||
p => {
|
||||
return Err(Error::new(
|
||||
p.span(),
|
||||
"Expected name of the Resource struct this method belongs to"
|
||||
))
|
||||
},
|
||||
};
|
||||
let resource_name = resource_path.segments.last().map(|s| s.ident.to_string())
|
||||
.ok_or_else(|| Error::new(resource_path.span(), "Resource name must not be empty"))?;
|
||||
|
||||
let resource_name = resource_path
|
||||
.segments
|
||||
.last()
|
||||
.map(|s| s.ident.to_string())
|
||||
.ok_or_else(|| Error::new(resource_path.span(), "Resource name must not be empty"))?;
|
||||
|
||||
let fun_ident = &fun.sig.ident;
|
||||
let fun_vis = &fun.vis;
|
||||
let fun_is_async = fun.sig.asyncness.is_some();
|
||||
|
||||
if let Some(unsafety) = fun.sig.unsafety
|
||||
{
|
||||
|
||||
if let Some(unsafety) = fun.sig.unsafety {
|
||||
return Err(Error::new(unsafety.span(), "Resource methods must not be unsafe"));
|
||||
}
|
||||
|
||||
|
||||
let trait_ident = method.trait_ident();
|
||||
let method_ident = method.fn_ident();
|
||||
let mod_ident = method.mod_ident(&resource_name);
|
||||
let handler_ident = method.handler_struct_ident(&resource_name);
|
||||
let setup_ident = method.setup_ident(&resource_name);
|
||||
|
||||
|
||||
let (ret, is_no_content) = match &fun.sig.output {
|
||||
ReturnType::Default => (quote!(#krate::NoContent), true),
|
||||
ReturnType::Type(_, ty) => (quote!(#ty), false)
|
||||
};
|
||||
|
||||
|
||||
// some default idents we'll need
|
||||
let state_ident = format_ident!("state");
|
||||
let repo_ident = format_ident!("repo");
|
||||
let conn_ident = format_ident!("conn");
|
||||
let auth_ident = format_ident!("auth");
|
||||
let res_ident = format_ident!("res");
|
||||
|
||||
|
||||
// extract arguments into pattern, ident and type
|
||||
let args = fun.sig.inputs.iter()
|
||||
let args = fun
|
||||
.sig
|
||||
.inputs
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, arg)| match arg {
|
||||
FnArg::Typed(arg) => interpret_arg(i, arg),
|
||||
FnArg::Receiver(_) => Err(Error::new(arg.span(), "Didn't expect self parameter"))
|
||||
})
|
||||
.collect_to_result()?;
|
||||
|
||||
|
||||
// extract the generic parameters to use
|
||||
let ty_names = method.type_names();
|
||||
let ty_len = ty_names.len();
|
||||
let generics_args : Vec<&MethodArgument> = args.iter()
|
||||
.filter(|arg| (*arg).ty.is_method_arg())
|
||||
.collect();
|
||||
if generics_args.len() > ty_len
|
||||
{
|
||||
let generics_args: Vec<&MethodArgument> = args.iter().filter(|arg| (*arg).ty.is_method_arg()).collect();
|
||||
if generics_args.len() > ty_len {
|
||||
return Err(Error::new(generics_args[ty_len].span(), "Too many arguments"));
|
||||
}
|
||||
else if generics_args.len() < ty_len
|
||||
{
|
||||
} else if generics_args.len() < ty_len {
|
||||
return Err(Error::new(fun_ident.span(), "Too few arguments"));
|
||||
}
|
||||
let generics : Vec<TokenStream> = generics_args.iter()
|
||||
let generics: Vec<TokenStream> = generics_args
|
||||
.iter()
|
||||
.map(|arg| arg.ty.quote_ty().unwrap())
|
||||
.zip(ty_names)
|
||||
.map(|(arg, name)| {
|
||||
|
@ -353,47 +334,53 @@ pub fn expand_method(method : Method, mut attrs : AttributeArgs, fun : ItemFn) -
|
|||
quote!(type #ident = #arg;)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
// extract the definition of our method
|
||||
let mut args_def : Vec<TokenStream> = args.iter()
|
||||
let mut args_def: Vec<TokenStream> = args
|
||||
.iter()
|
||||
.filter(|arg| (*arg).ty.is_method_arg())
|
||||
.map(|arg| {
|
||||
let ident = &arg.ident;
|
||||
let ty = arg.ty.quote_ty();
|
||||
quote!(#ident : #ty)
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
args_def.insert(0, quote!(mut #state_ident : #krate::State));
|
||||
|
||||
|
||||
// extract the arguments to pass over to the supplied method
|
||||
let args_pass : Vec<TokenStream> = args.iter().map(|arg| match (&arg.ty, &arg.ident) {
|
||||
(MethodArgumentType::StateRef, _) => quote!(&#state_ident),
|
||||
(MethodArgumentType::StateMutRef, _) => quote!(&mut #state_ident),
|
||||
(MethodArgumentType::MethodArg(_), ident) => quote!(#ident),
|
||||
(MethodArgumentType::DatabaseConnection(_), _) => quote!(&#conn_ident),
|
||||
(MethodArgumentType::AuthStatus(_), _) => quote!(#auth_ident),
|
||||
(MethodArgumentType::AuthStatusRef(_), _) => quote!(&#auth_ident)
|
||||
}).collect();
|
||||
|
||||
let args_pass: Vec<TokenStream> = args
|
||||
.iter()
|
||||
.map(|arg| match (&arg.ty, &arg.ident) {
|
||||
(MethodArgumentType::StateRef, _) => quote!(&#state_ident),
|
||||
(MethodArgumentType::StateMutRef, _) => quote!(&mut #state_ident),
|
||||
(MethodArgumentType::MethodArg(_), ident) => quote!(#ident),
|
||||
(MethodArgumentType::DatabaseConnection(_), _) => quote!(&#conn_ident),
|
||||
(MethodArgumentType::AuthStatus(_), _) => quote!(#auth_ident),
|
||||
(MethodArgumentType::AuthStatusRef(_), _) => quote!(&#auth_ident)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// prepare the method block
|
||||
let mut block = quote!(#fun_ident(#(#args_pass),*));
|
||||
let mut state_block = quote!();
|
||||
if fun_is_async
|
||||
{
|
||||
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_state_ref())
|
||||
{
|
||||
return Err(Error::new(arg.span(), "async fn must not take &State as an argument as State is not Sync, consider boxing"));
|
||||
if fun_is_async {
|
||||
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_state_ref()) {
|
||||
return Err(Error::new(
|
||||
arg.span(),
|
||||
"async fn must not take &State as an argument as State is not Sync, consider boxing"
|
||||
));
|
||||
}
|
||||
block = quote!(#block.await);
|
||||
}
|
||||
if is_no_content
|
||||
{
|
||||
if is_no_content {
|
||||
block = quote!(#block; Default::default())
|
||||
}
|
||||
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_database_conn())
|
||||
{
|
||||
if fun_is_async
|
||||
{
|
||||
return Err(Error::new(arg.span(), "async fn is not supported when database support is required, consider boxing"));
|
||||
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_database_conn()) {
|
||||
if fun_is_async {
|
||||
return Err(Error::new(
|
||||
arg.span(),
|
||||
"async fn is not supported when database support is required, consider boxing"
|
||||
));
|
||||
}
|
||||
let conn_ty = arg.ty.quote_ty();
|
||||
state_block = quote! {
|
||||
|
@ -411,70 +398,68 @@ pub fn expand_method(method : Method, mut attrs : AttributeArgs, fun : ItemFn) -
|
|||
}
|
||||
};
|
||||
}
|
||||
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_auth_status())
|
||||
{
|
||||
if let Some(arg) = args.iter().find(|arg| (*arg).ty.is_auth_status()) {
|
||||
let auth_ty = arg.ty.quote_ty();
|
||||
state_block = quote! {
|
||||
#state_block
|
||||
let #auth_ident : #auth_ty = <#auth_ty>::borrow_from(&#state_ident).clone();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// prepare the where clause
|
||||
let mut where_clause = quote!(#resource_path : #krate::Resource,);
|
||||
for arg in args.iter().filter(|arg| (*arg).ty.is_auth_status())
|
||||
{
|
||||
for arg in args.iter().filter(|arg| (*arg).ty.is_auth_status()) {
|
||||
let auth_ty = arg.ty.quote_ty();
|
||||
where_clause = quote!(#where_clause #auth_ty : Clone,);
|
||||
}
|
||||
|
||||
|
||||
// attribute generated code
|
||||
let operation_id = expand_operation_id(&attrs);
|
||||
let wants_auth = expand_wants_auth(&attrs, args.iter().any(|arg| (*arg).ty.is_auth_status()));
|
||||
|
||||
|
||||
// put everything together
|
||||
Ok(quote! {
|
||||
#fun
|
||||
|
||||
|
||||
#fun_vis mod #mod_ident
|
||||
{
|
||||
use super::*;
|
||||
|
||||
|
||||
struct #handler_ident;
|
||||
|
||||
|
||||
impl #krate::ResourceMethod for #handler_ident
|
||||
{
|
||||
type Res = #ret;
|
||||
|
||||
|
||||
#operation_id
|
||||
#wants_auth
|
||||
}
|
||||
|
||||
|
||||
impl #krate::#trait_ident for #handler_ident
|
||||
where #where_clause
|
||||
{
|
||||
#(#generics)*
|
||||
|
||||
|
||||
fn #method_ident(#(#args_def),*) -> std::pin::Pin<Box<dyn std::future::Future<Output = (#krate::State, #ret)> + Send>>
|
||||
{
|
||||
#[allow(unused_imports)]
|
||||
use #krate::{export::FutureExt, FromState};
|
||||
|
||||
|
||||
#state_block
|
||||
|
||||
|
||||
async move {
|
||||
let #res_ident = { #block };
|
||||
(#state_ident, #res_ident)
|
||||
}.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[deny(dead_code)]
|
||||
pub fn #setup_ident<D : #krate::DrawResourceRoutes>(route : &mut D)
|
||||
{
|
||||
route.#method_ident::<#handler_ident>();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,99 +1,77 @@
|
|||
use crate::util::{CollectToResult, remove_parens};
|
||||
use crate::util::{remove_parens, CollectToResult};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse_macro_input,
|
||||
spanned::Spanned,
|
||||
Attribute,
|
||||
AttributeArgs,
|
||||
Data,
|
||||
DataEnum,
|
||||
DataStruct,
|
||||
DeriveInput,
|
||||
Error,
|
||||
Field,
|
||||
Fields,
|
||||
Generics,
|
||||
GenericParam,
|
||||
Lit,
|
||||
LitStr,
|
||||
Meta,
|
||||
NestedMeta,
|
||||
Result,
|
||||
Variant
|
||||
parse_macro_input, spanned::Spanned, Attribute, AttributeArgs, Data, DataEnum, DataStruct, DeriveInput, Error, Field,
|
||||
Fields, GenericParam, Generics, Lit, LitStr, Meta, NestedMeta, Result, Variant
|
||||
};
|
||||
|
||||
pub fn expand_openapi_type(input : DeriveInput) -> Result<TokenStream>
|
||||
{
|
||||
pub fn expand_openapi_type(input: DeriveInput) -> Result<TokenStream> {
|
||||
match (input.ident, input.generics, input.attrs, input.data) {
|
||||
(ident, generics, attrs, Data::Enum(inum)) => expand_enum(ident, generics, attrs, inum),
|
||||
(ident, generics, attrs, Data::Struct(strukt)) => expand_struct(ident, generics, attrs, strukt),
|
||||
(_, _, _, Data::Union(uni)) => Err(Error::new(uni.union_token.span(), "#[derive(OpenapiType)] only works for structs and enums"))
|
||||
(_, _, _, Data::Union(uni)) => Err(Error::new(
|
||||
uni.union_token.span(),
|
||||
"#[derive(OpenapiType)] only works for structs and enums"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_where(generics : &Generics) -> TokenStream
|
||||
{
|
||||
if generics.params.is_empty()
|
||||
{
|
||||
fn expand_where(generics: &Generics) -> TokenStream {
|
||||
if generics.params.is_empty() {
|
||||
return quote!();
|
||||
}
|
||||
|
||||
|
||||
let krate = super::krate();
|
||||
let idents = generics.params.iter()
|
||||
let idents = generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match param {
|
||||
GenericParam::Type(ty) => Some(ty.ident.clone()),
|
||||
_ => None
|
||||
})
|
||||
.filter(|param| param.is_some())
|
||||
.map(|param| param.unwrap());
|
||||
|
||||
|
||||
quote! {
|
||||
where #(#idents : #krate::OpenapiType),*
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Attrs
|
||||
{
|
||||
nullable : bool,
|
||||
rename : Option<String>
|
||||
struct Attrs {
|
||||
nullable: bool,
|
||||
rename: Option<String>
|
||||
}
|
||||
|
||||
fn to_string(lit : &Lit) -> Result<String>
|
||||
{
|
||||
fn to_string(lit: &Lit) -> Result<String> {
|
||||
match lit {
|
||||
Lit::Str(str) => Ok(str.value()),
|
||||
_ => Err(Error::new(lit.span(), "Expected string literal"))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_bool(lit : &Lit) -> Result<bool>
|
||||
{
|
||||
fn to_bool(lit: &Lit) -> Result<bool> {
|
||||
match lit {
|
||||
Lit::Bool(bool) => Ok(bool.value),
|
||||
_ => Err(Error::new(lit.span(), "Expected bool"))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_attributes(input : &[Attribute]) -> Result<Attrs>
|
||||
{
|
||||
fn parse_attributes(input: &[Attribute]) -> Result<Attrs> {
|
||||
let mut parsed = Attrs::default();
|
||||
for attr in input
|
||||
{
|
||||
if attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("openapi".to_owned())
|
||||
{
|
||||
for attr in input {
|
||||
if attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("openapi".to_owned()) {
|
||||
let tokens = remove_parens(attr.tokens.clone());
|
||||
// TODO this is not public api but syn currently doesn't offer another convenient way to parse AttributeArgs
|
||||
let nested = parse_macro_input::parse::<AttributeArgs>(tokens.into())?;
|
||||
for meta in nested
|
||||
{
|
||||
for meta in nested {
|
||||
match &meta {
|
||||
NestedMeta::Meta(Meta::NameValue(kv)) => match kv.path.segments.last().map(|s| s.ident.to_string()) {
|
||||
Some(key) => match key.as_ref() {
|
||||
"nullable" => parsed.nullable = to_bool(&kv.lit)?,
|
||||
"rename" => parsed.rename = Some(to_string(&kv.lit)?),
|
||||
_ => return Err(Error::new(kv.path.span(), "Unknown key")),
|
||||
"rename" => parsed.rename = Some(to_string(&kv.lit)?),
|
||||
_ => return Err(Error::new(kv.path.span(), "Unknown key"))
|
||||
},
|
||||
_ => return Err(Error::new(meta.span(), "Unexpected token"))
|
||||
},
|
||||
|
@ -105,42 +83,40 @@ fn parse_attributes(input : &[Attribute]) -> Result<Attrs>
|
|||
Ok(parsed)
|
||||
}
|
||||
|
||||
fn expand_variant(variant : &Variant) -> Result<TokenStream>
|
||||
{
|
||||
if variant.fields != Fields::Unit
|
||||
{
|
||||
return Err(Error::new(variant.span(), "#[derive(OpenapiType)] does not support enum variants with fields"));
|
||||
fn expand_variant(variant: &Variant) -> Result<TokenStream> {
|
||||
if variant.fields != Fields::Unit {
|
||||
return Err(Error::new(
|
||||
variant.span(),
|
||||
"#[derive(OpenapiType)] does not support enum variants with fields"
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
let ident = &variant.ident;
|
||||
|
||||
|
||||
let attrs = parse_attributes(&variant.attrs)?;
|
||||
let name = match attrs.rename {
|
||||
Some(rename) => rename,
|
||||
None => ident.to_string()
|
||||
};
|
||||
|
||||
|
||||
Ok(quote! {
|
||||
enumeration.push(#name.to_string());
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_enum(ident : Ident, generics : Generics, attrs : Vec<Attribute>, input : DataEnum) -> Result<TokenStream>
|
||||
{
|
||||
fn expand_enum(ident: Ident, generics: Generics, attrs: Vec<Attribute>, input: DataEnum) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
let where_clause = expand_where(&generics);
|
||||
|
||||
|
||||
let attrs = parse_attributes(&attrs)?;
|
||||
let nullable = attrs.nullable;
|
||||
let name = match attrs.rename {
|
||||
Some(rename) => rename,
|
||||
None => ident.to_string()
|
||||
};
|
||||
|
||||
let variants = input.variants.iter()
|
||||
.map(expand_variant)
|
||||
.collect_to_result()?;
|
||||
|
||||
|
||||
let variants = input.variants.iter().map(expand_variant).collect_to_result()?;
|
||||
|
||||
Ok(quote! {
|
||||
impl #generics #krate::OpenapiType for #ident #generics
|
||||
#where_clause
|
||||
|
@ -148,11 +124,11 @@ fn expand_enum(ident : Ident, generics : Generics, attrs : Vec<Attribute>, input
|
|||
fn schema() -> #krate::OpenapiSchema
|
||||
{
|
||||
use #krate::{export::openapi::*, OpenapiSchema};
|
||||
|
||||
|
||||
let mut enumeration : Vec<String> = Vec::new();
|
||||
|
||||
|
||||
#(#variants)*
|
||||
|
||||
|
||||
let schema = SchemaKind::Type(Type::String(StringType {
|
||||
format: VariantOrUnknownOrEmpty::Empty,
|
||||
enumeration,
|
||||
|
@ -170,25 +146,29 @@ fn expand_enum(ident : Ident, generics : Generics, attrs : Vec<Attribute>, input
|
|||
})
|
||||
}
|
||||
|
||||
fn expand_field(field : &Field) -> Result<TokenStream>
|
||||
{
|
||||
fn expand_field(field: &Field) -> Result<TokenStream> {
|
||||
let ident = match &field.ident {
|
||||
Some(ident) => ident,
|
||||
None => return Err(Error::new(field.span(), "#[derive(OpenapiType)] does not support fields without an ident"))
|
||||
None => {
|
||||
return Err(Error::new(
|
||||
field.span(),
|
||||
"#[derive(OpenapiType)] does not support fields without an ident"
|
||||
))
|
||||
},
|
||||
};
|
||||
let ident_str = LitStr::new(&ident.to_string(), ident.span());
|
||||
let ty = &field.ty;
|
||||
|
||||
|
||||
let attrs = parse_attributes(&field.attrs)?;
|
||||
let nullable = attrs.nullable;
|
||||
let name = match attrs.rename {
|
||||
Some(rename) => rename,
|
||||
None => ident.to_string()
|
||||
};
|
||||
|
||||
|
||||
Ok(quote! {{
|
||||
let mut schema = <#ty>::schema();
|
||||
|
||||
|
||||
if schema.nullable
|
||||
{
|
||||
schema.nullable = false;
|
||||
|
@ -197,7 +177,7 @@ fn expand_field(field : &Field) -> Result<TokenStream>
|
|||
{
|
||||
required.push(#ident_str.to_string());
|
||||
}
|
||||
|
||||
|
||||
let keys : Vec<String> = schema.dependencies.keys().map(|k| k.to_string()).collect();
|
||||
for dep in keys
|
||||
{
|
||||
|
@ -207,7 +187,7 @@ fn expand_field(field : &Field) -> Result<TokenStream>
|
|||
dependencies.insert(dep, dep_schema);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
match schema.name.clone() {
|
||||
Some(schema_name) => {
|
||||
properties.insert(
|
||||
|
@ -226,42 +206,42 @@ fn expand_field(field : &Field) -> Result<TokenStream>
|
|||
}})
|
||||
}
|
||||
|
||||
fn expand_struct(ident : Ident, generics : Generics, attrs : Vec<Attribute>, input : DataStruct) -> Result<TokenStream>
|
||||
{
|
||||
fn expand_struct(ident: Ident, generics: Generics, attrs: Vec<Attribute>, input: DataStruct) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
let where_clause = expand_where(&generics);
|
||||
|
||||
|
||||
let attrs = parse_attributes(&attrs)?;
|
||||
let nullable = attrs.nullable;
|
||||
let name = match attrs.rename {
|
||||
Some(rename) => rename,
|
||||
None => ident.to_string()
|
||||
};
|
||||
|
||||
let fields : Vec<TokenStream> = match input.fields {
|
||||
Fields::Named(named_fields) => {
|
||||
named_fields.named.iter()
|
||||
.map(expand_field)
|
||||
.collect_to_result()?
|
||||
|
||||
let fields: Vec<TokenStream> = match input.fields {
|
||||
Fields::Named(named_fields) => named_fields.named.iter().map(expand_field).collect_to_result()?,
|
||||
Fields::Unnamed(fields) => {
|
||||
return Err(Error::new(
|
||||
fields.span(),
|
||||
"#[derive(OpenapiType)] does not support unnamed fields"
|
||||
))
|
||||
},
|
||||
Fields::Unnamed(fields) => return Err(Error::new(fields.span(), "#[derive(OpenapiType)] does not support unnamed fields")),
|
||||
Fields::Unit => Vec::new()
|
||||
};
|
||||
|
||||
Ok(quote!{
|
||||
|
||||
Ok(quote! {
|
||||
impl #generics #krate::OpenapiType for #ident #generics
|
||||
#where_clause
|
||||
{
|
||||
fn schema() -> #krate::OpenapiSchema
|
||||
{
|
||||
use #krate::{export::{openapi::*, IndexMap}, OpenapiSchema};
|
||||
|
||||
|
||||
let mut properties : IndexMap<String, ReferenceOr<Box<Schema>>> = IndexMap::new();
|
||||
let mut required : Vec<String> = Vec::new();
|
||||
let mut dependencies : IndexMap<String, OpenapiSchema> = IndexMap::new();
|
||||
|
||||
|
||||
#(#fields)*
|
||||
|
||||
|
||||
let schema = SchemaKind::Type(Type::Object(ObjectType {
|
||||
properties,
|
||||
required,
|
||||
|
|
|
@ -6,43 +6,34 @@ use syn::{
|
|||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
DeriveInput,
|
||||
Error,
|
||||
Generics,
|
||||
Path,
|
||||
Result,
|
||||
Token
|
||||
DeriveInput, Error, Generics, Path, Result, Token
|
||||
};
|
||||
|
||||
struct MimeList(Punctuated<Path, Token![,]>);
|
||||
|
||||
impl Parse for MimeList
|
||||
{
|
||||
fn parse(input: ParseStream) -> Result<Self>
|
||||
{
|
||||
impl Parse for MimeList {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let list = Punctuated::parse_separated_nonempty(&input)?;
|
||||
Ok(Self(list))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "openapi"))]
|
||||
fn impl_openapi_type(_ident : &Ident, _generics : &Generics) -> TokenStream
|
||||
{
|
||||
fn impl_openapi_type(_ident: &Ident, _generics: &Generics) -> TokenStream {
|
||||
quote!()
|
||||
}
|
||||
|
||||
#[cfg(feature = "openapi")]
|
||||
fn impl_openapi_type(ident : &Ident, generics : &Generics) -> TokenStream
|
||||
{
|
||||
fn impl_openapi_type(ident: &Ident, generics: &Generics) -> TokenStream {
|
||||
let krate = super::krate();
|
||||
|
||||
|
||||
quote! {
|
||||
impl #generics #krate::OpenapiType for #ident #generics
|
||||
{
|
||||
fn schema() -> #krate::OpenapiSchema
|
||||
{
|
||||
use #krate::{export::openapi::*, OpenapiSchema};
|
||||
|
||||
|
||||
OpenapiSchema::new(SchemaKind::Type(Type::String(StringType {
|
||||
format: VariantOrUnknownOrEmpty::Item(StringFormat::Binary),
|
||||
..Default::default()
|
||||
|
@ -52,32 +43,38 @@ fn impl_openapi_type(ident : &Ident, generics : &Generics) -> TokenStream
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expand_request_body(input : DeriveInput) -> Result<TokenStream>
|
||||
{
|
||||
pub fn expand_request_body(input: DeriveInput) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
let ident = input.ident;
|
||||
let generics = input.generics;
|
||||
|
||||
let types = input.attrs.into_iter()
|
||||
.filter(|attr| attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("supported_types".to_string()))
|
||||
|
||||
let types = input
|
||||
.attrs
|
||||
.into_iter()
|
||||
.filter(|attr| {
|
||||
attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("supported_types".to_string())
|
||||
})
|
||||
.flat_map(|attr| {
|
||||
let span = attr.span();
|
||||
attr.parse_args::<MimeList>()
|
||||
.map(|list| Box::new(list.0.into_iter().map(Ok)) as Box<dyn Iterator<Item = Result<Path>>>)
|
||||
.unwrap_or_else(|mut err| {
|
||||
err.combine(Error::new(span, "Hint: Types list should look like #[supported_types(TEXT_PLAIN, APPLICATION_JSON)]"));
|
||||
err.combine(Error::new(
|
||||
span,
|
||||
"Hint: Types list should look like #[supported_types(TEXT_PLAIN, APPLICATION_JSON)]"
|
||||
));
|
||||
Box::new(iter::once(Err(err)))
|
||||
})
|
||||
})
|
||||
.collect_to_result()?;
|
||||
|
||||
|
||||
let types = match types {
|
||||
ref types if types.is_empty() => quote!(None),
|
||||
types => quote!(Some(vec![#(#types),*]))
|
||||
};
|
||||
|
||||
|
||||
let impl_openapi_type = impl_openapi_type(&ident, &generics);
|
||||
|
||||
|
||||
Ok(quote! {
|
||||
impl #generics #krate::RequestBody for #ident #generics
|
||||
where #ident #generics : #krate::FromBody
|
||||
|
@ -87,7 +84,7 @@ pub fn expand_request_body(input : DeriveInput) -> Result<TokenStream>
|
|||
#types
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#impl_openapi_type
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,23 +1,18 @@
|
|||
use crate::{method::Method, util::CollectToResult};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::quote;
|
||||
use std::{iter, str::FromStr};
|
||||
use syn::{
|
||||
parenthesized,
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
DeriveInput,
|
||||
Error,
|
||||
Result,
|
||||
Token
|
||||
DeriveInput, Error, Result, Token
|
||||
};
|
||||
use std::{iter, str::FromStr};
|
||||
|
||||
struct MethodList(Punctuated<Ident, Token![,]>);
|
||||
|
||||
impl Parse for MethodList
|
||||
{
|
||||
fn parse(input: ParseStream) -> Result<Self>
|
||||
{
|
||||
impl Parse for MethodList {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let content;
|
||||
let _paren = parenthesized!(content in input);
|
||||
let list = Punctuated::parse_separated_nonempty(&content)?;
|
||||
|
@ -25,26 +20,32 @@ impl Parse for MethodList
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expand_resource(input : DeriveInput) -> Result<TokenStream>
|
||||
{
|
||||
pub fn expand_resource(input: DeriveInput) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
let ident = input.ident;
|
||||
let name = ident.to_string();
|
||||
|
||||
let methods = input.attrs.into_iter().filter(|attr|
|
||||
attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("resource".to_string()) // TODO wtf
|
||||
).map(|attr| {
|
||||
syn::parse2(attr.tokens).map(|m : MethodList| m.0.into_iter())
|
||||
}).flat_map(|list| match list {
|
||||
Ok(iter) => Box::new(iter.map(|method| {
|
||||
let method = Method::from_str(&method.to_string()).map_err(|err| Error::new(method.span(), err))?;
|
||||
let mod_ident = method.mod_ident(&name);
|
||||
let ident = method.setup_ident(&name);
|
||||
Ok(quote!(#mod_ident::#ident(&mut route);))
|
||||
})) as Box<dyn Iterator<Item = Result<TokenStream>>>,
|
||||
Err(err) => Box::new(iter::once(Err(err)))
|
||||
}).collect_to_result()?;
|
||||
|
||||
|
||||
let methods =
|
||||
input
|
||||
.attrs
|
||||
.into_iter()
|
||||
.filter(
|
||||
|attr| {
|
||||
attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("resource".to_string())
|
||||
} // TODO wtf
|
||||
)
|
||||
.map(|attr| syn::parse2(attr.tokens).map(|m: MethodList| m.0.into_iter()))
|
||||
.flat_map(|list| match list {
|
||||
Ok(iter) => Box::new(iter.map(|method| {
|
||||
let method = Method::from_str(&method.to_string()).map_err(|err| Error::new(method.span(), err))?;
|
||||
let mod_ident = method.mod_ident(&name);
|
||||
let ident = method.setup_ident(&name);
|
||||
Ok(quote!(#mod_ident::#ident(&mut route);))
|
||||
})) as Box<dyn Iterator<Item = Result<TokenStream>>>,
|
||||
Err(err) => Box::new(iter::once(Err(err)))
|
||||
})
|
||||
.collect_to_result()?;
|
||||
|
||||
Ok(quote! {
|
||||
impl #krate::Resource for #ident
|
||||
{
|
||||
|
|
|
@ -1,68 +1,54 @@
|
|||
use crate::util::{CollectToResult, remove_parens};
|
||||
use crate::util::{remove_parens, CollectToResult};
|
||||
use proc_macro2::{Ident, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use std::iter;
|
||||
use syn::{
|
||||
spanned::Spanned,
|
||||
Attribute,
|
||||
Data,
|
||||
DeriveInput,
|
||||
Error,
|
||||
Fields,
|
||||
GenericParam,
|
||||
LitStr,
|
||||
Path,
|
||||
PathSegment,
|
||||
Result,
|
||||
Type,
|
||||
spanned::Spanned, Attribute, Data, DeriveInput, Error, Fields, GenericParam, LitStr, Path, PathSegment, Result, Type,
|
||||
Variant
|
||||
};
|
||||
|
||||
|
||||
struct ErrorVariantField
|
||||
{
|
||||
attrs : Vec<Attribute>,
|
||||
ident : Ident,
|
||||
ty : Type
|
||||
struct ErrorVariantField {
|
||||
attrs: Vec<Attribute>,
|
||||
ident: Ident,
|
||||
ty: Type
|
||||
}
|
||||
|
||||
struct ErrorVariant
|
||||
{
|
||||
ident : Ident,
|
||||
status : Option<Path>,
|
||||
is_named : bool,
|
||||
fields : Vec<ErrorVariantField>,
|
||||
from_ty : Option<(usize, Type)>,
|
||||
display : Option<LitStr>
|
||||
struct ErrorVariant {
|
||||
ident: Ident,
|
||||
status: Option<Path>,
|
||||
is_named: bool,
|
||||
fields: Vec<ErrorVariantField>,
|
||||
from_ty: Option<(usize, Type)>,
|
||||
display: Option<LitStr>
|
||||
}
|
||||
|
||||
fn process_variant(variant : Variant) -> Result<ErrorVariant>
|
||||
{
|
||||
let status = match variant.attrs.iter()
|
||||
.find(|attr| attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("status".to_string()))
|
||||
{
|
||||
Some(attr) => Some(syn::parse2(remove_parens(attr.tokens.clone()))?),
|
||||
None => None
|
||||
};
|
||||
|
||||
fn process_variant(variant: Variant) -> Result<ErrorVariant> {
|
||||
let status =
|
||||
match variant.attrs.iter().find(|attr| {
|
||||
attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("status".to_string())
|
||||
}) {
|
||||
Some(attr) => Some(syn::parse2(remove_parens(attr.tokens.clone()))?),
|
||||
None => None
|
||||
};
|
||||
|
||||
let mut is_named = false;
|
||||
let mut fields = Vec::new();
|
||||
match variant.fields {
|
||||
Fields::Named(named) => {
|
||||
is_named = true;
|
||||
for field in named.named
|
||||
{
|
||||
for field in named.named {
|
||||
let span = field.span();
|
||||
fields.push(ErrorVariantField {
|
||||
attrs: field.attrs,
|
||||
ident: field.ident.ok_or_else(|| Error::new(span, "Missing ident for this enum variant field"))?,
|
||||
ident: field
|
||||
.ident
|
||||
.ok_or_else(|| Error::new(span, "Missing ident for this enum variant field"))?,
|
||||
ty: field.ty
|
||||
});
|
||||
}
|
||||
},
|
||||
Fields::Unnamed(unnamed) => {
|
||||
for (i, field) in unnamed.unnamed.into_iter().enumerate()
|
||||
{
|
||||
for (i, field) in unnamed.unnamed.into_iter().enumerate() {
|
||||
fields.push(ErrorVariantField {
|
||||
attrs: field.attrs,
|
||||
ident: format_ident!("arg{}", i),
|
||||
|
@ -72,19 +58,25 @@ fn process_variant(variant : Variant) -> Result<ErrorVariant>
|
|||
},
|
||||
Fields::Unit => {}
|
||||
}
|
||||
|
||||
let from_ty = fields.iter()
|
||||
|
||||
let from_ty = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, field)| field.attrs.iter().any(|attr| attr.path.segments.last().map(|segment| segment.ident.to_string()) == Some("from".to_string())))
|
||||
.find(|(_, field)| {
|
||||
field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|attr| attr.path.segments.last().map(|segment| segment.ident.to_string()) == Some("from".to_string()))
|
||||
})
|
||||
.map(|(i, field)| (i, field.ty.clone()));
|
||||
|
||||
let display = match variant.attrs.iter()
|
||||
.find(|attr| attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("display".to_string()))
|
||||
{
|
||||
|
||||
let display = match variant.attrs.iter().find(|attr| {
|
||||
attr.path.segments.iter().last().map(|segment| segment.ident.to_string()) == Some("display".to_string())
|
||||
}) {
|
||||
Some(attr) => Some(syn::parse2(remove_parens(attr.tokens.clone()))?),
|
||||
None => None
|
||||
};
|
||||
|
||||
|
||||
Ok(ErrorVariant {
|
||||
ident: variant.ident,
|
||||
status,
|
||||
|
@ -95,18 +87,15 @@ fn process_variant(variant : Variant) -> Result<ErrorVariant>
|
|||
})
|
||||
}
|
||||
|
||||
fn path_segment(name : &str) -> PathSegment
|
||||
{
|
||||
fn path_segment(name: &str) -> PathSegment {
|
||||
PathSegment {
|
||||
ident: format_ident!("{}", name),
|
||||
arguments: Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorVariant
|
||||
{
|
||||
fn fields_pat(&self) -> TokenStream
|
||||
{
|
||||
impl ErrorVariant {
|
||||
fn fields_pat(&self) -> TokenStream {
|
||||
let mut fields = self.fields.iter().map(|field| &field.ident).peekable();
|
||||
if fields.peek().is_none() {
|
||||
quote!()
|
||||
|
@ -116,74 +105,90 @@ impl ErrorVariant
|
|||
quote!( ( #( #fields ),* ) )
|
||||
}
|
||||
}
|
||||
|
||||
fn to_display_match_arm(&self, formatter_ident : &Ident, enum_ident : &Ident) -> Result<TokenStream>
|
||||
{
|
||||
|
||||
fn to_display_match_arm(&self, formatter_ident: &Ident, enum_ident: &Ident) -> Result<TokenStream> {
|
||||
let ident = &self.ident;
|
||||
let display = self.display.as_ref().ok_or_else(|| Error::new(self.ident.span(), "Missing display string for this variant"))?;
|
||||
|
||||
let display = self
|
||||
.display
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::new(self.ident.span(), "Missing display string for this variant"))?;
|
||||
|
||||
// lets find all required format parameters
|
||||
let display_str = display.value();
|
||||
let mut params : Vec<&str> = Vec::new();
|
||||
let mut params: Vec<&str> = Vec::new();
|
||||
let len = display_str.len();
|
||||
let mut start = len;
|
||||
let mut iter = display_str.chars().enumerate().peekable();
|
||||
while let Some((i, c)) = iter.next()
|
||||
{
|
||||
while let Some((i, c)) = iter.next() {
|
||||
// we found a new opening brace
|
||||
if start == len && c == '{'
|
||||
{
|
||||
if start == len && c == '{' {
|
||||
start = i + 1;
|
||||
}
|
||||
// we found a duplicate opening brace
|
||||
else if start == i && c == '{'
|
||||
{
|
||||
else if start == i && c == '{' {
|
||||
start = len;
|
||||
}
|
||||
// we found a closing brace
|
||||
else if start < i && c == '}'
|
||||
{
|
||||
else if start < i && c == '}' {
|
||||
match iter.peek() {
|
||||
Some((_, '}')) => return Err(Error::new(display.span(), "Error parsing format string: curly braces not allowed inside parameter name")),
|
||||
Some((_, '}')) => {
|
||||
return Err(Error::new(
|
||||
display.span(),
|
||||
"Error parsing format string: curly braces not allowed inside parameter name"
|
||||
))
|
||||
},
|
||||
_ => params.push(&display_str[start..i])
|
||||
};
|
||||
start = len;
|
||||
}
|
||||
// we found a closing brace without content
|
||||
else if start == i && c == '}'
|
||||
{
|
||||
return Err(Error::new(display.span(), "Error parsing format string: parameter name must not be empty"))
|
||||
else if start == i && c == '}' {
|
||||
return Err(Error::new(
|
||||
display.span(),
|
||||
"Error parsing format string: parameter name must not be empty"
|
||||
));
|
||||
}
|
||||
}
|
||||
if start != len
|
||||
{
|
||||
return Err(Error::new(display.span(), "Error parsing format string: Unmatched opening brace"));
|
||||
if start != len {
|
||||
return Err(Error::new(
|
||||
display.span(),
|
||||
"Error parsing format string: Unmatched opening brace"
|
||||
));
|
||||
}
|
||||
let params = params.into_iter().map(|name| format_ident!("{}{}", if self.is_named { "" } else { "arg" }, name));
|
||||
|
||||
let params = params
|
||||
.into_iter()
|
||||
.map(|name| format_ident!("{}{}", if self.is_named { "" } else { "arg" }, name));
|
||||
|
||||
let fields_pat = self.fields_pat();
|
||||
Ok(quote! {
|
||||
#enum_ident::#ident #fields_pat => write!(#formatter_ident, #display #(, #params = #params)*)
|
||||
})
|
||||
}
|
||||
|
||||
fn into_match_arm(self, krate : &TokenStream, enum_ident : &Ident) -> Result<TokenStream>
|
||||
{
|
||||
|
||||
fn into_match_arm(self, krate: &TokenStream, enum_ident: &Ident) -> Result<TokenStream> {
|
||||
let ident = &self.ident;
|
||||
let fields_pat = self.fields_pat();
|
||||
let status = self.status.map(|status| {
|
||||
// the status might be relative to StatusCode, so let's fix that
|
||||
if status.leading_colon.is_none() && status.segments.len() < 2
|
||||
{
|
||||
if status.leading_colon.is_none() && status.segments.len() < 2 {
|
||||
let status_ident = status.segments.first().cloned().unwrap_or_else(|| path_segment("OK"));
|
||||
Path {
|
||||
leading_colon: Some(Default::default()),
|
||||
segments: vec![path_segment("gotham_restful"), path_segment("gotham"), path_segment("hyper"), path_segment("StatusCode"), status_ident].into_iter().collect()
|
||||
segments: vec![
|
||||
path_segment("gotham_restful"),
|
||||
path_segment("gotham"),
|
||||
path_segment("hyper"),
|
||||
path_segment("StatusCode"),
|
||||
status_ident,
|
||||
]
|
||||
.into_iter()
|
||||
.collect()
|
||||
}
|
||||
} else {
|
||||
status
|
||||
}
|
||||
else { status }
|
||||
});
|
||||
|
||||
|
||||
// the response will come directly from the from_ty if present
|
||||
let res = match (self.from_ty, status) {
|
||||
(Some((from_index, _)), None) => {
|
||||
|
@ -198,14 +203,13 @@ impl ErrorVariant
|
|||
})),
|
||||
(None, None) => return Err(Error::new(ident.span(), "Missing #[status(code)] for this variant"))
|
||||
};
|
||||
|
||||
|
||||
Ok(quote! {
|
||||
#enum_ident::#ident #fields_pat => #res
|
||||
})
|
||||
}
|
||||
|
||||
fn were(&self) -> Option<TokenStream>
|
||||
{
|
||||
|
||||
fn were(&self) -> Option<TokenStream> {
|
||||
match self.from_ty.as_ref() {
|
||||
Some((_, ty)) => Some(quote!( #ty : ::std::error::Error )),
|
||||
None => None
|
||||
|
@ -213,22 +217,22 @@ impl ErrorVariant
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expand_resource_error(input : DeriveInput) -> Result<TokenStream>
|
||||
{
|
||||
pub fn expand_resource_error(input: DeriveInput) -> Result<TokenStream> {
|
||||
let krate = super::krate();
|
||||
let ident = input.ident;
|
||||
let generics = input.generics;
|
||||
|
||||
|
||||
let inum = match input.data {
|
||||
Data::Enum(inum) => Ok(inum),
|
||||
Data::Struct(strukt) => Err(strukt.struct_token.span()),
|
||||
Data::Union(uni) => Err(uni.union_token.span())
|
||||
}.map_err(|span| Error::new(span, "#[derive(ResourceError)] only works for enums"))?;
|
||||
let variants = inum.variants.into_iter()
|
||||
.map(process_variant)
|
||||
.collect_to_result()?;
|
||||
|
||||
let display_impl = if variants.iter().any(|v| v.display.is_none()) { None } else {
|
||||
}
|
||||
.map_err(|span| Error::new(span, "#[derive(ResourceError)] only works for enums"))?;
|
||||
let variants = inum.variants.into_iter().map(process_variant).collect_to_result()?;
|
||||
|
||||
let display_impl = if variants.iter().any(|v| v.display.is_none()) {
|
||||
None
|
||||
} else {
|
||||
let were = generics.params.iter().filter_map(|param| match param {
|
||||
GenericParam::Type(ty) => {
|
||||
let ident = &ty.ident;
|
||||
|
@ -237,7 +241,8 @@ pub fn expand_resource_error(input : DeriveInput) -> Result<TokenStream>
|
|||
_ => None
|
||||
});
|
||||
let formatter_ident = format_ident!("resource_error_display_formatter");
|
||||
let match_arms = variants.iter()
|
||||
let match_arms = variants
|
||||
.iter()
|
||||
.map(|v| v.to_display_match_arm(&formatter_ident, &ident))
|
||||
.collect_to_result()?;
|
||||
Some(quote! {
|
||||
|
@ -253,34 +258,39 @@ pub fn expand_resource_error(input : DeriveInput) -> Result<TokenStream>
|
|||
}
|
||||
})
|
||||
};
|
||||
|
||||
let mut from_impls : Vec<TokenStream> = Vec::new();
|
||||
|
||||
for var in &variants
|
||||
{
|
||||
|
||||
let mut from_impls: Vec<TokenStream> = Vec::new();
|
||||
|
||||
for var in &variants {
|
||||
let var_ident = &var.ident;
|
||||
let (from_index, from_ty) = match var.from_ty.as_ref() {
|
||||
Some(f) => f,
|
||||
None => continue
|
||||
};
|
||||
let from_ident = &var.fields[*from_index].ident;
|
||||
|
||||
|
||||
let fields_pat = var.fields_pat();
|
||||
let fields_where = var.fields.iter().enumerate()
|
||||
let fields_where = var
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| i != from_index)
|
||||
.map(|(_, field)| {
|
||||
let ty = &field.ty;
|
||||
quote!( #ty : Default )
|
||||
})
|
||||
.chain(iter::once(quote!( #from_ty : ::std::error::Error )));
|
||||
let fields_let = var.fields.iter().enumerate()
|
||||
let fields_let = var
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(i, _)| i != from_index)
|
||||
.map(|(_, field)| {
|
||||
let id = &field.ident;
|
||||
let ty = &field.ty;
|
||||
quote!( let #id : #ty = Default::default(); )
|
||||
});
|
||||
|
||||
|
||||
from_impls.push(quote! {
|
||||
impl #generics ::std::convert::From<#from_ty> for #ident #generics
|
||||
where #( #fields_where ),*
|
||||
|
@ -293,20 +303,21 @@ pub fn expand_resource_error(input : DeriveInput) -> Result<TokenStream>
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
let were = variants.iter().filter_map(|variant| variant.were()).collect::<Vec<_>>();
|
||||
let variants = variants.into_iter()
|
||||
let variants = variants
|
||||
.into_iter()
|
||||
.map(|variant| variant.into_match_arm(&krate, &ident))
|
||||
.collect_to_result()?;
|
||||
|
||||
.collect_to_result()?;
|
||||
|
||||
Ok(quote! {
|
||||
#display_impl
|
||||
|
||||
|
||||
impl #generics #krate::IntoResponseError for #ident #generics
|
||||
where #( #were ),*
|
||||
{
|
||||
type Err = #krate::export::serde_json::Error;
|
||||
|
||||
|
||||
fn into_response_error(self) -> Result<#krate::Response, Self::Err>
|
||||
{
|
||||
match self {
|
||||
|
@ -314,7 +325,7 @@ pub fn expand_resource_error(input : DeriveInput) -> Result<TokenStream>
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#( #from_impls )*
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,40 +2,38 @@ use proc_macro2::{Delimiter, TokenStream, TokenTree};
|
|||
use std::iter;
|
||||
use syn::Error;
|
||||
|
||||
pub trait CollectToResult
|
||||
{
|
||||
pub trait CollectToResult {
|
||||
type Item;
|
||||
|
||||
|
||||
fn collect_to_result(self) -> Result<Vec<Self::Item>, Error>;
|
||||
}
|
||||
|
||||
impl<Item, I> CollectToResult for I
|
||||
where
|
||||
I : Iterator<Item = Result<Item, Error>>
|
||||
I: Iterator<Item = Result<Item, Error>>
|
||||
{
|
||||
type Item = Item;
|
||||
|
||||
fn collect_to_result(self) -> Result<Vec<Item>, Error>
|
||||
{
|
||||
self.fold(<Result<Vec<Item>, Error>>::Ok(Vec::new()), |res, code| {
|
||||
match (code, res) {
|
||||
(Ok(code), Ok(mut codes)) => { codes.push(code); Ok(codes) },
|
||||
(Ok(_), Err(errors)) => Err(errors),
|
||||
(Err(err), Ok(_)) => Err(err),
|
||||
(Err(err), Err(mut errors)) => { errors.combine(err); Err(errors) }
|
||||
}
|
||||
|
||||
fn collect_to_result(self) -> Result<Vec<Item>, Error> {
|
||||
self.fold(<Result<Vec<Item>, Error>>::Ok(Vec::new()), |res, code| match (code, res) {
|
||||
(Ok(code), Ok(mut codes)) => {
|
||||
codes.push(code);
|
||||
Ok(codes)
|
||||
},
|
||||
(Ok(_), Err(errors)) => Err(errors),
|
||||
(Err(err), Ok(_)) => Err(err),
|
||||
(Err(err), Err(mut errors)) => {
|
||||
errors.combine(err);
|
||||
Err(errors)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn remove_parens(input : TokenStream) -> TokenStream
|
||||
{
|
||||
pub fn remove_parens(input: TokenStream) -> TokenStream {
|
||||
let iter = input.into_iter().flat_map(|tt| {
|
||||
if let TokenTree::Group(group) = &tt
|
||||
{
|
||||
if group.delimiter() == Delimiter::Parenthesis
|
||||
{
|
||||
if let TokenTree::Group(group) = &tt {
|
||||
if group.delimiter() == Delimiter::Parenthesis {
|
||||
return Box::new(group.stream().into_iter()) as Box<dyn Iterator<Item = TokenTree>>;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue