cbf16c5a33
* Bootstrap * Upd * Bootstrap macro * Revert stuff * Correct PoC to compile * Bootstrap #[graphql_interface] expansion * Bootstrap #[graphql_interface] meta parsing * Bootstrap #[graphql_interface] very basic code generation [skip ci] * Upd trait code generation and fix keywords usage [skip ci] * Expand trait impls [skip ci] * Tune up objects [skip ci] * Finally! Complies at least... [skip ci] * Parse meta for fields and its arguments [skip ci] - also, refactor and bikeshed new macros code * Impl filling fields meta and bootstrap field resolution [skip ci] * Poking with fields resolution [skip ci] * Solve Rust's teen async HRTB problems [skip ci] * Start parsing trait methods [skip ci] * Finish parsing fields from trait methods [skip ci] * Autodetect trait asyncness and allow to specify it [skip ci] * Allow to autogenerate trait object alias via attribute * Support generics in trait definition and asyncify them correctly * Temporary disable explicit async * Cover arguments and custom names/descriptions in tests * Re-enable tests with explicit async and fix the codegen to satisfy it * Check implementers are registered in schema and vice versa * Check argument camelCases * Test argument defaults, and allow Into coercions for them * Re-enable markers * Re-enable markers and relax Sized requirement on IsInputType/IsOutputType marker traits * Revert 'juniper_actix' fmt * Fix missing marks for object * Fix subscriptions marks * Deduce result type correctly via traits * Final fixes * Fmt * Restore marks checking * Support custom ScalarValue * Cover deprecations with tests * Impl dowcasting via methods * Impl dowcasting via external functions * Support custom context, vol. 1 * Support custom context, vol. 2 * Cover fallible field with test * Impl explicit generic ScalarValue, vol.1 * Impl explicit generic ScalarValue, vol.2 * Allow passing executor into methods * Generating enum, vol.1 * Generating enum, vol.2 * Generating enum, vol.3 * Generating enum, vol.3 * Generating enum, vol.4 * Generating enum, vol.5 * Generating enum, vol.6 * Generating enum, vol.7 * Generating enum, vol.8 * Refactor juniper stuff * Fix juniper tests, vol.1 * Fix juniper tests, vol.2 * Polish 'juniper' crate changes, vol.1 * Polish 'juniper' crate changes, vol.2 * Remove redundant stuf * Polishing 'juniper_codegen', vol.1 * Polishing 'juniper_codegen', vol.2 * Polishing 'juniper_codegen', vol.3 * Polishing 'juniper_codegen', vol.4 * Polishing 'juniper_codegen', vol.5 * Polishing 'juniper_codegen', vol.6 * Polishing 'juniper_codegen', vol.7 * Polishing 'juniper_codegen', vol.8 * Polishing 'juniper_codegen', vol.9 * Fix other crates tests and make Clippy happier * Fix examples * Add codegen failure tests, vol. 1 * Add codegen failure tests, vol. 2 * Add codegen failure tests, vol.3 * Fix codegen failure tests accordingly to latest nightly Rust * Fix codegen when interface has no implementers * Fix warnings in book tests * Describing new interfaces in Book, vol.1 Co-authored-by: Christian Legnitto <LegNeato@users.noreply.github.com>
148 lines
4.7 KiB
Rust
148 lines
4.7 KiB
Rust
use proc_macro2::TokenStream;
|
|
use quote::quote;
|
|
use syn::{ext::IdentExt, spanned::Spanned, Data, Fields};
|
|
|
|
use crate::{
|
|
result::{GraphQLScope, UnsupportedAttribute},
|
|
util::{self, span_container::SpanContainer},
|
|
};
|
|
|
|
pub fn impl_enum(ast: syn::DeriveInput, error: GraphQLScope) -> syn::Result<TokenStream> {
|
|
let ast_span = ast.span();
|
|
|
|
if !ast.generics.params.is_empty() {
|
|
return Err(error.custom_error(ast_span, "does not support generics or lifetimes"));
|
|
}
|
|
|
|
let variants = match ast.data {
|
|
Data::Enum(enum_data) => enum_data.variants,
|
|
_ => return Err(error.custom_error(ast_span, "can only be applied to enums")),
|
|
};
|
|
|
|
// Parse attributes.
|
|
let attrs = util::ObjectAttributes::from_attrs(&ast.attrs)?;
|
|
let ident = &ast.ident;
|
|
let name = attrs
|
|
.name
|
|
.clone()
|
|
.map(SpanContainer::into_inner)
|
|
.unwrap_or_else(|| ident.unraw().to_string());
|
|
|
|
let fields = variants
|
|
.into_iter()
|
|
.filter_map(|field| {
|
|
let span = field.span();
|
|
let field_attrs = match util::FieldAttributes::from_attrs(
|
|
&field.attrs,
|
|
util::FieldAttributeParseMode::Object,
|
|
) {
|
|
Ok(attrs) => attrs,
|
|
Err(err) => {
|
|
proc_macro_error::emit_error!(err);
|
|
return None;
|
|
}
|
|
};
|
|
|
|
let field_name = field.ident;
|
|
let name = field_attrs
|
|
.name
|
|
.clone()
|
|
.map(SpanContainer::into_inner)
|
|
.unwrap_or_else(|| util::to_upper_snake_case(&field_name.unraw().to_string()));
|
|
|
|
let resolver_code = quote!( #ident::#field_name );
|
|
|
|
let _type = match field.fields {
|
|
Fields::Unit => syn::parse_str(&field_name.to_string()).unwrap(),
|
|
_ => {
|
|
error.emit_custom(
|
|
field.fields.span(),
|
|
"all fields of the enum must be unnamed, e.g., None",
|
|
);
|
|
return None;
|
|
}
|
|
};
|
|
|
|
if let Some(skip) = field_attrs.skip {
|
|
error.unsupported_attribute(skip.span(), UnsupportedAttribute::Skip);
|
|
return None;
|
|
}
|
|
|
|
if name.starts_with("__") {
|
|
error.no_double_underscore(if let Some(name) = field_attrs.name {
|
|
name.span_ident()
|
|
} else {
|
|
field_name.span()
|
|
});
|
|
}
|
|
|
|
if let Some(default) = field_attrs.default {
|
|
error.unsupported_attribute_within(
|
|
default.span_ident(),
|
|
UnsupportedAttribute::Default,
|
|
);
|
|
}
|
|
|
|
Some(util::GraphQLTypeDefinitionField {
|
|
name,
|
|
_type,
|
|
args: Vec::new(),
|
|
description: field_attrs.description.map(SpanContainer::into_inner),
|
|
deprecation: field_attrs.deprecation.map(SpanContainer::into_inner),
|
|
resolver_code,
|
|
is_type_inferred: true,
|
|
is_async: false,
|
|
default: None,
|
|
span,
|
|
})
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
proc_macro_error::abort_if_dirty();
|
|
|
|
if fields.is_empty() {
|
|
error.not_empty(ast_span);
|
|
}
|
|
if let Some(duplicates) =
|
|
crate::util::duplicate::Duplicate::find_by_key(&fields, |field| &field.name)
|
|
{
|
|
error.duplicate(duplicates.iter())
|
|
}
|
|
|
|
if !attrs.interfaces.is_empty() {
|
|
attrs.interfaces.iter().for_each(|elm| {
|
|
error.unsupported_attribute(elm.span(), UnsupportedAttribute::Interface)
|
|
});
|
|
}
|
|
|
|
if let Some(scalar) = attrs.scalar {
|
|
error.unsupported_attribute(scalar.span_ident(), UnsupportedAttribute::Scalar);
|
|
}
|
|
|
|
if !attrs.is_internal && name.starts_with("__") {
|
|
error.no_double_underscore(if let Some(name) = attrs.name {
|
|
name.span_ident()
|
|
} else {
|
|
ident.span()
|
|
});
|
|
}
|
|
|
|
proc_macro_error::abort_if_dirty();
|
|
|
|
let definition = util::GraphQLTypeDefiniton {
|
|
name,
|
|
_type: syn::parse_str(&ast.ident.to_string()).unwrap(),
|
|
context: attrs.context.map(SpanContainer::into_inner),
|
|
scalar: None,
|
|
description: attrs.description.map(SpanContainer::into_inner),
|
|
fields,
|
|
// NOTICE: only unit variants allow -> no generics possible
|
|
generics: syn::Generics::default(),
|
|
interfaces: vec![],
|
|
include_type_generics: true,
|
|
generic_scalar: true,
|
|
no_async: attrs.no_async.is_some(),
|
|
};
|
|
|
|
Ok(definition.into_enum_tokens())
|
|
}
|