juniper/juniper_codegen/src/result.rs
Kai Ren ddc1488195
Codegen reimplementation for GraphQL unions (#666)
- reimplement #[derive(GraphQLUnion)] macro to support:
    - both structs and enums
    - generics in type definition
    - multiple #[graphql] attributes
    - external resolver functions
- remove From trait impls generation for enum variants

- reimplement #[graphql_union] macro to support:
    - traits
    - generics in trait definition
    - multiple attributes
    - external resolver functions
    - GraphQLType implemetation for a raw trait object
    - GraphQLTypeAsync implemetation (#549)

- add marker::GraphQLUnion trait

- rewrite "2.5 Unions" section in Book (Juniper user documentation)

- rewrite `codegen` and `codegen_fail` integration tests for GraphQL unions

Additionally:
- re-export `futures` crate in `juniper` for convenient reuse in the generated code without requiring library user to provide `futures` crate by himself (#663)
- use unit type () as default context for EmptyMutation and EmptySubscriptions
- relax Sized trait bound on some GraphQLType and GraphQLTypeAsync definitions, implementations and usages
2020-06-04 11:19:01 +03:00

144 lines
4.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//!
use crate::util::duplicate::Duplicate;
use proc_macro2::Span;
use proc_macro_error::{Diagnostic, Level};
use std::fmt;
/// URL of the GraphQL specification (June 2018 Edition).
pub const SPEC_URL: &'static str = "https://spec.graphql.org/June2018/";
#[allow(unused_variables)]
pub enum GraphQLScope {
UnionAttr,
DeriveObject,
DeriveInputObject,
UnionDerive,
DeriveEnum,
DeriveScalar,
ImplScalar,
ImplObject,
}
impl GraphQLScope {
pub fn spec_section(&self) -> &str {
match self {
Self::DeriveObject | Self::ImplObject => "#sec-Objects",
Self::DeriveInputObject => "#sec-Input-Objects",
Self::UnionAttr | Self::UnionDerive => "#sec-Unions",
Self::DeriveEnum => "#sec-Enums",
Self::DeriveScalar | Self::ImplScalar => "#sec-Scalars",
}
}
}
impl fmt::Display for GraphQLScope {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match self {
Self::DeriveObject | Self::ImplObject => "object",
Self::DeriveInputObject => "input object",
Self::UnionAttr | Self::UnionDerive => "union",
Self::DeriveEnum => "enum",
Self::DeriveScalar | Self::ImplScalar => "scalar",
};
write!(f, "GraphQL {}", name)
}
}
#[allow(unused_variables)]
#[derive(Debug)]
pub enum UnsupportedAttribute {
Skip,
Interface,
Scalar,
Deprecation,
Default,
}
impl GraphQLScope {
fn spec_link(&self) -> String {
format!("{}{}", SPEC_URL, self.spec_section())
}
pub fn custom<S: AsRef<str>>(&self, span: Span, msg: S) -> Diagnostic {
Diagnostic::spanned(span, Level::Error, format!("{} {}", self, msg.as_ref()))
.note(self.spec_link())
}
pub fn emit_custom<S: AsRef<str>>(&self, span: Span, msg: S) {
self.custom(span, msg).emit()
}
pub fn custom_error<S: AsRef<str>>(&self, span: Span, msg: S) -> syn::Error {
syn::Error::new(span, format!("{} {}", self, msg.as_ref()))
}
pub fn unsupported_attribute(&self, attribute: Span, kind: UnsupportedAttribute) {
Diagnostic::spanned(
attribute,
Level::Error,
format!("attribute `{:?}` can not be used at the top level of {}", kind, self),
)
.note("The macro is known to Juniper. However, not all valid #[graphql] attributes are available for each macro".to_string())
.emit();
}
pub fn unsupported_attribute_within(&self, attribute: Span, kind: UnsupportedAttribute) {
Diagnostic::spanned(
attribute,
Level::Error,
format!("attribute `{:?}` can not be used inside of {}", kind, self),
)
.note("The macro is known to Juniper. However, not all valid #[graphql] attributes are available for each macro".to_string())
.emit();
}
pub fn not_empty(&self, container: Span) {
Diagnostic::spanned(
container,
Level::Error,
format!("{} expects at least one field", self),
)
.note(self.spec_link())
.emit();
}
pub fn duplicate<'a, T: syn::spanned::Spanned + 'a>(
&self,
duplicates: impl IntoIterator<Item = &'a Duplicate<T>>,
) {
duplicates
.into_iter()
.for_each(|dup| {
(&dup.spanned[1..])
.iter()
.for_each(|spanned| {
Diagnostic::spanned(
spanned.span(),
Level::Error,
format!(
"{} does not allow fields with the same name",
self
),
)
.help(format!("There is at least one other field with the same name `{}`, possibly renamed via the #[graphql] attribute", dup.name))
.note(self.spec_link())
.emit();
});
})
}
pub fn no_double_underscore(&self, field: Span) {
Diagnostic::spanned(
field,
Level::Error,
"All types and directives defined within a schema must not have a name which begins \
with `__` (two underscores), as this is used exclusively by GraphQLs introspection \
system."
.into(),
)
.note(format!("{}#sec-Schema", SPEC_URL))
.emit();
}
}