async-graphql/derive/src/output_type.rs
2020-10-14 10:25:41 +08:00

82 lines
2.8 KiB
Rust

use proc_macro2::{Ident, Span};
use quote::quote;
use syn::{Error, GenericArgument, PathArguments, Result, Type};
pub enum OutputType<'a> {
Value(&'a Type),
Result(&'a Type, &'a Type),
}
impl<'a> OutputType<'a> {
pub fn parse(input: &'a Type) -> Result<Self> {
let ty = if let Type::Path(p) = input {
if p.path.segments.last().unwrap().ident == "Result"
|| p.path.segments.last().unwrap().ident == "FieldResult"
{
if let PathArguments::AngleBracketed(args) =
&p.path.segments.last().unwrap().arguments
{
if args.args.is_empty() {
return Err(Error::new_spanned(input, "Invalid type"));
}
let mut res = None;
for arg in &args.args {
if let GenericArgument::Type(value_ty) = arg {
res = Some(OutputType::Result(input, value_ty));
break;
}
}
if res.is_none() {
return Err(Error::new_spanned(input, "Invalid type"));
}
res.unwrap()
} else {
return Err(Error::new_spanned(input, "Invalid type"));
}
} else {
OutputType::Value(input)
}
} else {
OutputType::Value(input)
};
Ok(ty)
}
pub fn value_type(&self) -> Type {
let tokens = match self {
OutputType::Value(ty) => quote! {#ty},
OutputType::Result(_, ty) => quote! {#ty},
};
let mut ty = syn::parse2::<syn::Type>(tokens).unwrap();
Self::remove_lifecycle(&mut ty);
ty
}
fn remove_lifecycle(ty: &mut Type) {
match ty {
Type::Reference(r) => {
r.lifetime = None;
Self::remove_lifecycle(&mut r.elem);
}
Type::Path(r) => {
for s in &mut r.path.segments {
if let PathArguments::AngleBracketed(args) = &mut s.arguments {
for arg in &mut args.args {
match arg {
GenericArgument::Lifetime(lt) => {
lt.ident = Ident::new("_", Span::call_site());
}
GenericArgument::Type(ty) => {
Self::remove_lifecycle(ty);
}
_ => {}
}
}
}
}
}
_ => {}
}
}
}