juniper_from_schema/lib.rs
1//! This library contains a procedural macro that reads a GraphQL schema file, and generates the
2//! corresponding [Juniper](https://crates.io/crates/juniper) [macro calls]. This means you can
3//! have a real schema file and be guaranteed that it matches your Rust implementation. It also
4//! removes most of the boilerplate involved in using Juniper.
5//!
6//! [macro calls]: https://graphql-rust.github.io/types/objects/complex_fields.html
7//!
8//! # Table of contents
9//!
10//! - [Example](#example)
11//! - [Example web app](#example-web-app)
12//! - [GraphQL features](#graphql-features)
13//! - [The `ID` type](#the-id-type)
14//! - [Custom scalar types](#custom-scalar-types)
15//! - [Special case scalars](#special-case-scalars)
16//! - [Interfaces](#interfaces)
17//! - [Union types](#union-types)
18//! - [Input objects](#input-objects)
19//! - [Enumeration types](#enumeration-types)
20//! - [Default argument values](#default-argument-values)
21//! - [Supported schema directives](#supported-schema-directives)
22//! - [Definition for `@juniper`](#definition-for-juniper)
23//! - [Customizing ownership](#customizing-ownership)
24//! - [Infallible fields](#infallible-fields)
25//! - [GraphQL to Rust types](#graphql-to-rust-types)
26//! - [Query trails](#query-trails)
27//! - [Abbreviated example](#abbreviated-example)
28//! - [Types](#types)
29//! - [Downcasting for interface and union `QueryTrail`s](#downcasting-for-interface-and-union-querytrails)
30//! - [`QueryTrail`s for fields that take arguments](#querytrails-for-fields-that-take-arguments)
31//! - [Customizing the error type](#customizing-the-error-type)
32//! - [Customizing the context type](#customizing-the-context-type)
33//! - [Inspecting the generated code](#inspecting-the-generated-code)
34//!
35//! # Example
36//!
37//! Schema:
38//!
39//! ```graphql
40//! schema {
41//! query: Query
42//! mutation: Mutation
43//! }
44//!
45//! type Query {
46//! // The directive makes the return value `FieldResult<String>`
47//! // rather than the default `FieldResult<&String>`
48//! helloWorld(name: String!): String! @juniper(ownership: "owned")
49//! }
50//!
51//! type Mutation {
52//! noop: Boolean!
53//! }
54//! ```
55//!
56//! How you could implement that schema:
57//!
58//! ```
59//! #[macro_use]
60//! extern crate juniper;
61//!
62//! use juniper_from_schema::graphql_schema_from_file;
63//!
64//! // This is the important line
65//! graphql_schema_from_file!("tests/schemas/doc_schema.graphql");
66//!
67//! pub struct Context;
68//! impl juniper::Context for Context {}
69//!
70//! pub struct Query;
71//!
72//! impl QueryFields for Query {
73//! fn field_hello_world(
74//! &self,
75//! executor: &juniper::Executor<'_, Context>,
76//! name: String,
77//! ) -> juniper::FieldResult<String> {
78//! Ok(format!("Hello, {}!", name))
79//! }
80//! }
81//!
82//! pub struct Mutation;
83//!
84//! impl MutationFields for Mutation {
85//! fn field_noop(&self, executor: &juniper::Executor<'_, Context>) -> juniper::FieldResult<&bool> {
86//! Ok(&true)
87//! }
88//! }
89//!
90//! fn main() {
91//! let ctx = Context;
92//!
93//! let query = "query { helloWorld(name: \"Ferris\") }";
94//!
95//! let (result, errors) = juniper::execute(
96//! query,
97//! None,
98//! &Schema::new(Query, Mutation),
99//! &juniper::Variables::new(),
100//! &ctx,
101//! )
102//! .unwrap();
103//!
104//! assert_eq!(errors.len(), 0);
105//! assert_eq!(
106//! result
107//! .as_object_value()
108//! .unwrap()
109//! .get_field_value("helloWorld")
110//! .unwrap()
111//! .as_scalar_value::<String>()
112//! .unwrap(),
113//! "Hello, Ferris!",
114//! );
115//! }
116//! ```
117//!
118//! And with `graphql_schema_from_file!` expanded your code would look something like this:
119//!
120//! ```
121//! #[macro_use]
122//! extern crate juniper;
123//!
124//! pub struct Context;
125//! impl juniper::Context for Context {}
126//!
127//! pub struct Query;
128//!
129//! juniper::graphql_object!(Query: Context |&self| {
130//! field hello_world(&executor, name: String) -> juniper::FieldResult<String> {
131//! <Self as QueryFields>::field_hello_world(&self, &executor, name)
132//! }
133//! });
134//!
135//! trait QueryFields {
136//! fn field_hello_world(
137//! &self,
138//! executor: &juniper::Executor<'_, Context>,
139//! name: String,
140//! ) -> juniper::FieldResult<String>;
141//! }
142//!
143//! impl QueryFields for Query {
144//! fn field_hello_world(
145//! &self,
146//! executor: &juniper::Executor<'_, Context>,
147//! name: String,
148//! ) -> juniper::FieldResult<String> {
149//! Ok(format!("Hello, {}!", name))
150//! }
151//! }
152//!
153//! pub struct Mutation;
154//!
155//! juniper::graphql_object!(Mutation: Context |&self| {
156//! field noop(&executor) -> juniper::FieldResult<&bool> {
157//! <Self as MutationFields>::field_noop(&self, &executor)
158//! }
159//! });
160//!
161//! trait MutationFields {
162//! fn field_noop(&self, executor: &juniper::Executor<'_, Context>) -> juniper::FieldResult<&bool>;
163//! }
164//!
165//! impl MutationFields for Mutation {
166//! fn field_noop(&self, executor: &juniper::Executor<'_, Context>) -> juniper::FieldResult<&bool> {
167//! Ok(&true)
168//! }
169//! }
170//!
171//! type Schema = juniper::RootNode<'static, Query, Mutation>;
172//!
173//! fn main() {
174//! let ctx = Context;
175//!
176//! let query = "query { helloWorld(name: \"Ferris\") }";
177//!
178//! let (result, errors) = juniper::execute(
179//! query,
180//! None,
181//! &Schema::new(Query, Mutation),
182//! &juniper::Variables::new(),
183//! &ctx,
184//! )
185//! .unwrap();
186//!
187//! assert_eq!(errors.len(), 0);
188//! assert_eq!(
189//! result
190//! .as_object_value()
191//! .unwrap()
192//! .get_field_value("helloWorld")
193//! .unwrap()
194//! .as_scalar_value::<String>()
195//! .unwrap(),
196//! "Hello, Ferris!",
197//! );
198//! }
199//! ```
200//!
201//! # Example web app
202//!
203//! You can find an example of how to use this library together with [Rocket] and [Diesel] to make
204//! a GraphQL web app at <https://github.com/davidpdrsn/graphql-app-example> or an example of how
205//! to use this library with [Actix] and [Diesel] at
206//! <https://github.com/husseinraoouf/graphql-actix-example>.
207//!
208//! [Rocket]: https://rocket.rs
209//! [Diesel]: http://diesel.rs
210//! [Actix]: https://actix.rs/
211//!
212//! # GraphQL features
213//!
214//! The goal of this library is to support as much of GraphQL as Juniper does.
215//!
216//! Here is the complete list of features:
217//!
218//! Supported:
219//! - Object types including converting lists and non-nulls to Rust types
220//! - Custom scalar types including the `ID` type
221//! - Interfaces
222//! - Unions
223//! - Input objects
224//! - Enumeration types
225//!
226//! Not supported yet:
227//! - Subscriptions (will be supported once Juniper supports subscriptions)
228//! - Type extensions
229//!
230//! ## The `ID` type
231//!
232//! The `ID` GraphQL type will be generated into [`juniper::ID`].
233//!
234//! [`juniper::ID`]: https://docs.rs/juniper/latest/juniper/struct.ID.html
235//!
236//! ## Custom scalar types
237//!
238//! Custom scalar types will be generated into a newtype wrapper around a `String`. For example:
239//!
240//! ```graphql
241//! scalar Cursor
242//! ```
243//!
244//! Would result in
245//!
246//! ```
247//! pub struct Cursor(pub String);
248//! ```
249//!
250//! ## Special case scalars
251//!
252//! A couple of scalar names have special meaning. Those are:
253//!
254//! - `Url` becomes
255//! [`url::Url`](https://docs.rs/url/2.1.0/url/struct.Url.html).
256//! - `Uuid` becomes
257//! [`uuid::Uuid`](https://docs.rs/uuid/0.7.4/uuid/struct.Uuid.html).
258//! - `Date` becomes
259//! [`chrono::naive::NaiveDate`](https://docs.rs/chrono/0.4.6/chrono/naive/struct.NaiveDate.html).
260//! - `DateTimeUtc` becomes [`chrono::DateTime<chrono::offset::Utc>`] by default but if defined with
261//! `scalar DateTimeUtc @juniper(with_time_zone: false)` it will become [`chrono::naive::NaiveDateTime`].
262//!
263//! Juniper doesn't support [`chrono::Date`](https://docs.rs/chrono/0.4.9/chrono/struct.Date.html)
264//! so therefore this library cannot support that either. You can read about Juniper's supported
265//! integrations [here](https://docs.rs/juniper/0.13.1/juniper/integrations/index.html).
266//!
267//! [`chrono::DateTime<chrono::offset::Utc>`]: https://docs.rs/chrono/0.4.9/chrono/struct.DateTime.html
268//! [`chrono::naive::NaiveDateTime`]: https://docs.rs/chrono/0.4.9/chrono/naive/struct.NaiveDateTime.html
269//!
270//! ## Interfaces
271//!
272//! Juniper has several ways of representing GraphQL interfaces in Rust. They are listed
273//! [here](https://graphql-rust.github.io/types/interfaces.html#enums) along with their advantages
274//! and disadvantages.
275//!
276//! For the generated code we use the `enum` pattern because we found it to be the most flexible.
277//!
278//! Abbreviated example (find [complete example here](https://github.com/davidpdrsn/juniper-from-schema/blob/master/examples/interface.rs)):
279//!
280//! ```
281//! # #[macro_use]
282//! # extern crate juniper;
283//! # use juniper::*;
284//! # use juniper_from_schema::graphql_schema;
285//! # fn main() {}
286//! # pub struct Context;
287//! # impl juniper::Context for Context {}
288//! # pub struct Article { id: ID, text: String }
289//! # impl ArticleFields for Article {
290//! # fn field_id(
291//! # &self,
292//! # executor: &Executor<'_, Context>,
293//! # ) -> FieldResult<&ID> { unimplemented!() }
294//! # fn field_text(
295//! # &self,
296//! # executor: &Executor<'_, Context>,
297//! # ) -> FieldResult<&String> { unimplemented!() }
298//! # }
299//! # pub struct Tweet { id: ID, text: String }
300//! # impl TweetFields for Tweet {
301//! # fn field_id(
302//! # &self,
303//! # executor: &Executor<'_, Context>,
304//! # ) -> FieldResult<&ID> { unimplemented!() }
305//! # fn field_text(
306//! # &self,
307//! # executor: &Executor<'_, Context>,
308//! # ) -> FieldResult<&String> { unimplemented!() }
309//! # }
310//! #
311//! graphql_schema! {
312//! schema {
313//! query: Query
314//! }
315//!
316//! type Query {
317//! search(query: String!): [SearchResult!]! @juniper(ownership: "owned")
318//! }
319//!
320//! interface SearchResult {
321//! id: ID!
322//! text: String!
323//! }
324//!
325//! type Article implements SearchResult {
326//! id: ID!
327//! text: String!
328//! }
329//!
330//! type Tweet implements SearchResult {
331//! id: ID!
332//! text: String!
333//! }
334//! }
335//!
336//! pub struct Query;
337//!
338//! impl QueryFields for Query {
339//! fn field_search(
340//! &self,
341//! executor: &Executor<'_, Context>,
342//! trail: &QueryTrail<'_, SearchResult, juniper_from_schema::Walked>,
343//! query: String,
344//! ) -> FieldResult<Vec<SearchResult>> {
345//! let article: Article = Article { id: ID::new("1"), text: "Business".to_string() };
346//! let tweet: Tweet = Tweet { id: ID::new("2"), text: "1 weird tip".to_string() };
347//!
348//! let posts = vec![
349//! SearchResult::from(article),
350//! SearchResult::from(tweet),
351//! ];
352//!
353//! Ok(posts)
354//! }
355//! }
356//! ```
357//!
358//! The enum that gets generated has variants for each type that implements the interface and also
359//! implements `From<T>` for each type.
360//!
361//! ## Union types
362//!
363//! Union types are basically just interfaces so they work in very much the same way.
364//!
365//! Abbreviated example (find [complete example here](https://github.com/davidpdrsn/juniper-from-schema/blob/master/examples/union_types.rs)):
366//!
367//! ```
368//! # #[macro_use]
369//! # extern crate juniper;
370//! # use juniper::*;
371//! # use juniper_from_schema::graphql_schema;
372//! # fn main() {}
373//! # pub struct Context;
374//! # impl juniper::Context for Context {}
375//! # pub struct Article { id: ID, text: String }
376//! # impl ArticleFields for Article {
377//! # fn field_id(
378//! # &self,
379//! # executor: &Executor<'_, Context>,
380//! # ) -> FieldResult<&ID> { unimplemented!() }
381//! # fn field_text(
382//! # &self,
383//! # executor: &Executor<'_, Context>,
384//! # ) -> FieldResult<&String> { unimplemented!() }
385//! # }
386//! # pub struct Tweet { id: ID, text: String }
387//! # impl TweetFields for Tweet {
388//! # fn field_id(
389//! # &self,
390//! # executor: &Executor<'_, Context>,
391//! # ) -> FieldResult<&ID> { unimplemented!() }
392//! # fn field_text(
393//! # &self,
394//! # executor: &Executor<'_, Context>,
395//! # ) -> FieldResult<&String> { unimplemented!() }
396//! # }
397//! #
398//! graphql_schema! {
399//! schema {
400//! query: Query
401//! }
402//!
403//! type Query {
404//! search(query: String!): [SearchResult!]! @juniper(ownership: "owned")
405//! }
406//!
407//! union SearchResult = Article | Tweet
408//!
409//! type Article {
410//! id: ID!
411//! text: String!
412//! }
413//!
414//! type Tweet {
415//! id: ID!
416//! text: String!
417//! }
418//! }
419//!
420//! pub struct Query;
421//!
422//! impl QueryFields for Query {
423//! fn field_search(
424//! &self,
425//! executor: &Executor<'_, Context>,
426//! trail: &QueryTrail<'_, SearchResult, juniper_from_schema::Walked>,
427//! query: String,
428//! ) -> FieldResult<Vec<SearchResult>> {
429//! let article: Article = Article { id: ID::new("1"), text: "Business".to_string() };
430//! let tweet: Tweet = Tweet { id: ID::new("2"), text: "1 weird tip".to_string() };
431//!
432//! let posts = vec![
433//! SearchResult::from(article),
434//! SearchResult::from(tweet),
435//! ];
436//!
437//! Ok(posts)
438//! }
439//! }
440//! ```
441//!
442//! ## Input objects
443//!
444//! Input objects will be converted into Rust structs with public fields.
445//!
446//! Abbreviated example (find [complete example here](https://github.com/davidpdrsn/juniper-from-schema/blob/master/examples/input_types.rs)):
447//!
448//! ```
449//! # #[macro_use]
450//! # extern crate juniper;
451//! # use juniper::*;
452//! # use juniper_from_schema::graphql_schema;
453//! # fn main() {}
454//! # pub struct Context;
455//! # impl juniper::Context for Context {}
456//! # pub struct Post { id: ID }
457//! # impl PostFields for Post {
458//! # fn field_id(
459//! # &self,
460//! # executor: &Executor<'_, Context>,
461//! # ) -> FieldResult<&ID> {
462//! # unimplemented!()
463//! # }
464//! # fn field_title(
465//! # &self,
466//! # executor: &Executor<'_, Context>,
467//! # ) -> FieldResult<&String> {
468//! # unimplemented!()
469//! # }
470//! # }
471//! # pub struct Query;
472//! # impl QueryFields for Query {
473//! # fn field_noop(
474//! # &self,
475//! # executor: &Executor<'_, Context>,
476//! # ) -> FieldResult<&bool> {
477//! # unimplemented!()
478//! # }
479//! # }
480//! graphql_schema! {
481//! schema {
482//! query: Query
483//! mutation: Mutation
484//! }
485//!
486//! type Mutation {
487//! createPost(input: CreatePost!): Post @juniper(ownership: "owned")
488//! }
489//!
490//! input CreatePost {
491//! title: String!
492//! }
493//!
494//! type Post {
495//! id: ID!
496//! title: String!
497//! }
498//!
499//! type Query { noop: Boolean! }
500//! }
501//!
502//! pub struct Mutation;
503//!
504//! impl MutationFields for Mutation {
505//! fn field_create_post(
506//! &self,
507//! executor: &Executor<'_, Context>,
508//! trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
509//! input: CreatePost,
510//! ) -> FieldResult<Option<Post>> {
511//! let title: String = input.title;
512//!
513//! unimplemented!()
514//! }
515//! }
516//! ```
517//!
518//! From that example `CreatePost` will be defined as
519//!
520//! ```
521//! pub struct CreatePost {
522//! pub title: String,
523//! }
524//! ```
525//!
526//! ## Enumeration types
527//!
528//! GraphQL enumeration types will be converted into normal Rust enums. The name of each variant
529//! will be camel cased.
530//!
531//! Abbreviated example (find [complete example here](https://github.com/davidpdrsn/juniper-from-schema/blob/master/examples/enumeration_types.rs)):
532//!
533//! ```
534//! # #[macro_use]
535//! # extern crate juniper;
536//! # use juniper::*;
537//! # use juniper_from_schema::graphql_schema;
538//! # fn main() {}
539//! # pub struct Context;
540//! # impl juniper::Context for Context {}
541//! # pub struct Post { id: ID }
542//! # impl PostFields for Post {
543//! # fn field_id(
544//! # &self,
545//! # executor: &Executor<'_, Context>,
546//! # ) -> FieldResult<&ID> {
547//! # Ok(&self.id)
548//! # }
549//! # }
550//! #
551//! graphql_schema! {
552//! schema {
553//! query: Query
554//! }
555//!
556//! enum Status {
557//! PUBLISHED
558//! UNPUBLISHED
559//! }
560//!
561//! type Query {
562//! allPosts(status: Status!): [Post!]! @juniper(ownership: "owned")
563//! }
564//!
565//! type Post {
566//! id: ID!
567//! }
568//! }
569//!
570//! pub struct Query;
571//!
572//! impl QueryFields for Query {
573//! fn field_all_posts(
574//! &self,
575//! executor: &Executor<'_, Context>,
576//! trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
577//! status: Status,
578//! ) -> FieldResult<Vec<Post>> {
579//! match status {
580//! Status::Published => unimplemented!("find published posts"),
581//! Status::Unpublished => unimplemented!("find unpublished posts"),
582//! }
583//! }
584//! }
585//! ```
586//!
587//! ## Default argument values
588//!
589//! In GraphQL you are able to provide default values for field arguments, provided the argument is
590//! nullable.
591//!
592//! Arguments of the following types support default values:
593//! - `Float`
594//! - `Int`
595//! - `String`
596//! - `Boolean`
597//! - Enumerations
598//! - Input objects (as field arguments, see below)
599//! - Lists containing some other supported type
600//!
601//! Abbreviated example (find [complete example here](https://github.com/davidpdrsn/juniper-from-schema/blob/master/examples/default_argument_values.rs)):
602//!
603//! ```
604//! # #[macro_use]
605//! # extern crate juniper;
606//! # use juniper::*;
607//! # use juniper_from_schema::graphql_schema;
608//! # fn main() {}
609//! # pub struct Context;
610//! # impl juniper::Context for Context {}
611//! # pub struct Post { id: ID }
612//! # impl PostFields for Post {
613//! # fn field_id(
614//! # &self,
615//! # executor: &Executor<'_, Context>,
616//! # ) -> FieldResult<&ID> {
617//! # Ok(&self.id)
618//! # }
619//! # }
620//! #
621//! graphql_schema! {
622//! schema {
623//! query: Query
624//! }
625//!
626//! enum Status {
627//! PUBLISHED
628//! UNPUBLISHED
629//! }
630//!
631//! input Pagination {
632//! pageSize: Int!
633//! cursor: ID
634//! }
635//!
636//! type Query {
637//! allPosts(
638//! status: Status = PUBLISHED,
639//! pagination: Pagination = { pageSize: 20 }
640//! ): [Post!]! @juniper(ownership: "owned")
641//! }
642//!
643//! type Post {
644//! id: ID!
645//! }
646//! }
647//!
648//! pub struct Query;
649//!
650//! impl QueryFields for Query {
651//! fn field_all_posts(
652//! &self,
653//! executor: &Executor<'_, Context>,
654//! trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
655//! status: Status,
656//! pagination: Pagination,
657//! ) -> FieldResult<Vec<Post>> {
658//! // `status` will be `Status::Published` if not given in the query
659//!
660//! match status {
661//! Status::Published => unimplemented!("find published posts"),
662//! Status::Unpublished => unimplemented!("find unpublished posts"),
663//! }
664//! }
665//! }
666//! ```
667//!
668//! ### Input object gotchas
669//!
670//! Defaults for input objects are only supported as field arguments. The following is not
671//! supported
672//!
673//! ```graphql
674//! input SomeType {
675//! field: Int = 1
676//! }
677//! ```
678//!
679//! This isn't supported because [the spec is unclear about how to handle multiple nested
680//! defaults](https://github.com/webonyx/graphql-php/issues/350).
681//!
682//! Also, defaults are only used if no arguments are passed. So given the schema
683//!
684//! ```graphql
685//! input Input {
686//! a: String
687//! b: String
688//! }
689//!
690//! type Query {
691//! field(arg: Input = { a: "a" }): Int!
692//! }
693//! ```
694//!
695//! and the query
696//!
697//! ```graphql
698//! query MyQuery {
699//! field(arg: { b: "my b" })
700//! }
701//! ```
702//!
703//! The value of `arg` inside the resolver would be `Input { a: None, b: Some("my b") }`. Note that
704//! even though `a` has a default value in the field doesn't get used here because we set `arg` in
705//! the query.
706//!
707//! # Supported schema directives
708//!
709//! A number of [schema directives][] are supported that lets you customize the generated code:
710//!
711//! - `@juniper(ownership: "owned|borrowed|as_ref")`. For customizing ownership of returned data.
712//! More info [here](#customizing-ownership).
713//! - `@juniper(infallible: true|false)`. Customize if a field should return `Result<T, _>` or
714//! just `T`. More info
715//! [here](http://localhost:4000/juniper_from_schema/index.html#infallible-fields).
716//! - `@deprecated`. For deprecating types in your schema. Also supports supplying a reason with
717//! `@deprecated(reason: "...")`
718//!
719//! [schema directives]: https://www.apollographql.com/docs/apollo-server/schema/directives/
720//!
721//! ## Definition for `@juniper`
722//!
723//! Some tools that operate on your GraphQL schema require you to include the definition for all
724//! directives used. So in case you need it the definition for `@juniper` is:
725//!
726//! ```graphql
727//! directive @juniper(
728//! ownership: String = "borrowed",
729//! infallible: Boolean = false,
730//! with_time_zone: Boolean = true
731//! ) on FIELD_DEFINITION
732//! ```
733//!
734//! This directive definition is allowed in your schema, as well as any other directive definition.
735//! Definitions of `@juniper` that differ from this are not allowed though.
736//!
737//! The definition might change in future versions. Please refer to the [changelog][].
738//!
739//! juniper-from-schema doesn't require to put this in your schema, so you only need to include it
740//! if some other tool requires it.
741//!
742//! [changelog]: https://github.com/davidpdrsn/juniper-from-schema/blob/master/CHANGELOG.md
743//!
744//! ## Customizing ownership
745//!
746//! By default all fields return borrowed values. Specifically the type is
747//! `juniper::FieldResult<&'a T>` where `'a` is the lifetime of `self`. This works well for
748//! returning data owned by `self` and avoids needless `.clone()` calls you would need if fields
749//! returned owned values.
750//!
751//! However if you need to change the ownership you have to add the directive
752//! `@juniper(ownership:)` to the field in the schema.
753//!
754//! It takes the following arguments:
755//!
756//! - `@juniper(ownership: "borrowed")`: The data returned will be borrowed from `self`
757//! (`FieldResult<&T>`).
758//! - `@juniper(ownership: "owned")`: The return type will be owned (`FieldResult<T>`).
759//! - `@juniper(ownership: "as_ref")`: Only applicable for `Option` and `Vec` return types. Changes
760//! the inner type to be borrowed (`FieldResult<Option<&T>>` or `FieldResult<Vec<&T>>`).
761//!
762//! Example:
763//!
764//! ```
765//! # #[macro_use]
766//! # extern crate juniper;
767//! # use juniper_from_schema::*;
768//! # use juniper::*;
769//! # pub struct Context;
770//! # impl juniper::Context for Context {}
771//! # fn main() {}
772//! graphql_schema! {
773//! schema {
774//! query: Query
775//! }
776//!
777//! type Query {
778//! borrowed: String!
779//! owned: String! @juniper(ownership: "owned")
780//! asRef: String @juniper(ownership: "as_ref")
781//! }
782//! }
783//!
784//! pub struct Query;
785//!
786//! impl QueryFields for Query {
787//! fn field_borrowed(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
788//! // ...
789//! # unimplemented!()
790//! }
791//!
792//! fn field_owned(&self, _: &Executor<'_, Context>) -> FieldResult<String> {
793//! // ...
794//! # unimplemented!()
795//! }
796//!
797//! fn field_as_ref(&self, _: &Executor<'_, Context>) -> FieldResult<Option<&String>> {
798//! // ...
799//! # unimplemented!()
800//! }
801//! }
802//! ```
803//!
804//! All field arguments will be owned.
805//!
806//! ## Infallible fields
807//!
808//! By default the generated resolvers are fallible, meaining they return a `Result<T, _>` rather
809//! than a bare `T`. You can customize that using `@juniper(infallible: true)`.
810//!
811//! Example:
812//!
813//! ```
814//! # #[macro_use]
815//! # extern crate juniper;
816//! # use juniper_from_schema::*;
817//! # use juniper::*;
818//! # pub struct Context;
819//! # impl juniper::Context for Context {}
820//! # fn main() {}
821//! graphql_schema! {
822//! schema {
823//! query: Query
824//! }
825//!
826//! type Query {
827//! canError: String!
828//! cannotError: String! @juniper(infallible: true)
829//! cannotErrorAndOwned: String! @juniper(infallible: true, ownership: "owned")
830//! }
831//! }
832//!
833//! pub struct Query;
834//!
835//! impl QueryFields for Query {
836//! fn field_can_error(&self, _: &Executor<'_, Context>) -> FieldResult<&String> {
837//! // ...
838//! # unimplemented!()
839//! }
840//!
841//! fn field_cannot_error(&self, _: &Executor<'_, Context>) -> &String {
842//! // ...
843//! # unimplemented!()
844//! }
845//!
846//! fn field_cannot_error_and_owned(&self, _: &Executor<'_, Context>) -> String {
847//! // ...
848//! # unimplemented!()
849//! }
850//! }
851//! ```
852//!
853//! # GraphQL to Rust types
854//!
855//! This is how the standard GraphQL types will be mapped to Rust:
856//!
857//! - `Int` -> `i32`
858//! - `Float` -> `f64`
859//! - `String` -> `String`
860//! - `Boolean` -> `bool`
861//! - `ID` -> [`juniper::ID`](https://docs.rs/juniper/latest/juniper/struct.ID.html)
862//!
863//! # Query trails
864//!
865//! If you're not careful about preloading associations for deeply nested queries you risk getting
866//! lots of [N+1 query bugs][]. Juniper provides a [look ahead API][] which lets you inspect things
867//! coming up further down a query. However the API is string based, so you risk making typos and
868//! checking for fields that don't exist.
869//!
870//! `QueryTrail` is a thin wrapper around Juniper look aheads with generated methods for each field
871//! on all your types. This means the compiler will reject your code if you're checking for invalid
872//! fields.
873//!
874//! Resolver methods (`field_*`) that return object types (non scalar values) will also get a
875//! `QueryTrail` argument besides the executor.
876//!
877//! Since the `QueryTrail` type itself is defined in this crate (rather than being inserted into
878//! your code) we cannot directly add methods for your GraphQL fields. Those methods have to be
879//! added through ["extension traits"](http://xion.io/post/code/rust-extension-traits.html). So if
880//! you see an error like
881//!
882//! ```text
883//! | trail.foo();
884//! | ^^^ method not found in `&juniper_from_schema::QueryTrail<'a, User, juniper_from_schema::Walked>`
885//! |
886//! = help: items from traits can only be used if the trait is in scope
887//! help: the following trait is implemented but not in scope, perhaps add a `use` for it:
888//! |
889//! 2 | use crate::graphql_schema::query_trails::QueryTrailUserExtensions;
890//! |
891//! ```
892//!
893//! Then adding `use crate::graphql_schema::query_trails::*` to you module should fix it. This is
894//! necessary because all the extention traits are generated inside a module called `query_trails`.
895//! This is done so you can glob import the `QueryTrail` extension traits without glob importing
896//! everything from your GraphQL schema.
897//!
898//! If you just want everything from the schema `use crate::graphql_schema::*` will also bring in
899//! the extension traits.
900//!
901//! [N+1 query bugs]: https://secure.phabricator.com/book/phabcontrib/article/n_plus_one/
902//! [look ahead API]: https://docs.rs/juniper/0.11.1/juniper/struct.LookAheadSelection.html
903//!
904//! ## Abbreviated example
905//!
906//! Find [complete example here](https://github.com/davidpdrsn/juniper-from-schema/blob/master/examples/query_trails.rs)
907//!
908//! ```
909//! # #[macro_use]
910//! # extern crate juniper;
911//! # use juniper::*;
912//! # use juniper_from_schema::graphql_schema;
913//! # fn main() {}
914//! # pub struct Context;
915//! # impl juniper::Context for Context {}
916//! #
917//! graphql_schema! {
918//! schema {
919//! query: Query
920//! }
921//!
922//! type Query {
923//! allPosts: [Post!]! @juniper(ownership: "owned")
924//! }
925//!
926//! type Post {
927//! id: Int!
928//! author: User!
929//! }
930//!
931//! type User {
932//! id: Int!
933//! }
934//! }
935//!
936//! pub struct Query;
937//!
938//! impl QueryFields for Query {
939//! fn field_all_posts(
940//! &self,
941//! executor: &Executor<'_, Context>,
942//! trail: &QueryTrail<'_, Post, juniper_from_schema::Walked>,
943//! ) -> FieldResult<Vec<Post>> {
944//! // Check if the query includes the author
945//! if let Some(_) = trail.author().walk() {
946//! // Somehow preload the users to avoid N+1 query bugs
947//! // Exactly how to do this depends on your setup
948//! }
949//!
950//! // Normally this would come from the database
951//! let post = Post {
952//! id: 1,
953//! author: User { id: 1 },
954//! };
955//!
956//! Ok(vec![post])
957//! }
958//! }
959//!
960//! pub struct Post {
961//! id: i32,
962//! author: User,
963//! }
964//!
965//! impl PostFields for Post {
966//! fn field_id(&self, executor: &Executor<'_, Context>) -> FieldResult<&i32> {
967//! Ok(&self.id)
968//! }
969//!
970//! fn field_author(
971//! &self,
972//! executor: &Executor<'_, Context>,
973//! trail: &QueryTrail<'_, User, juniper_from_schema::Walked>,
974//! ) -> FieldResult<&User> {
975//! Ok(&self.author)
976//! }
977//! }
978//!
979//! pub struct User {
980//! id: i32,
981//! }
982//!
983//! impl UserFields for User {
984//! fn field_id(
985//! &self,
986//! executor: &Executor<'_, Context>,
987//! ) -> FieldResult<&i32> {
988//! Ok(&self.id)
989//! }
990//! }
991//! ```
992//!
993//! ## Types
994//!
995//! A query trail has two generic parameters: `QueryTrail<'a, T, K>`. `T` is the type the current
996//! field returns and `K` is either `Walked` or `NotWalked`.
997//!
998//! The lifetime `'a` comes from Juniper and is the lifetime of the incoming query.
999//!
1000//! ### `T`
1001//!
1002//! The `T` allows us to implement different methods for different types. For example in the
1003//! example above we implement `id` and `author` for `QueryTrail<'_, Post, K>` but only `id` for
1004//! `QueryTrail<'_, User, K>`.
1005//!
1006//! If your field returns a `Vec<T>` or `Option<T>` the given query trail will be `QueryTrail<'_,
1007//! T, _>`. So `Vec` or `Option` will be removed and you'll only be given the inner most type.
1008//! That is because in the GraphQL query syntax it doesn't matter if you're querying a `User`
1009//! or `[User]`. The fields you have access to are the same.
1010//!
1011//! ### `K`
1012//!
1013//! The `Walked` and `NotWalked` types are used to check if a given trail has been checked to
1014//! actually be part of a query. Calling any method on a `QueryTrail<'_, T, K>` will return
1015//! `QueryTrail<'_, T, NotWalked>`, and to check if the trail is actually part of the query you have
1016//! to call `.walk()` which returns `Option<QueryTrail<'_, T, Walked>>`. If that is a `Some(_)`
1017//! you'll know the trail is part of the query and you can do whatever preloading is necessary.
1018//!
1019//! Example:
1020//!
1021//! ```ignore
1022//! if let Some(walked_trail) = trail
1023//! .some_field()
1024//! .some_other_field()
1025//! .third_field()
1026//! .walk()
1027//! {
1028//! // preload stuff
1029//! }
1030//! ```
1031//!
1032//! You can always run `cargo doc` and inspect all the methods on `QueryTrail` and in which
1033//! contexts you can call them.
1034//!
1035//! ## Downcasting for interface and union `QueryTrail`s
1036//!
1037//! _This section is mostly relevant if you're using
1038//! [juniper-eager-loading](https://crates.io/crates/juniper-eager-loading) however it isn't
1039//! specific to that library._
1040//!
1041//! If you have a `QueryTrail<'a, T, Walked>` where `T` is an interface or union type you can use
1042//! `.downcast()` to convert that `QueryTrail` into one of the implementors of the interface or
1043//! union.
1044//!
1045//! Example:
1046//!
1047//! ```
1048//! # #[macro_use]
1049//! # extern crate juniper;
1050//! # use juniper::*;
1051//! # use juniper_from_schema::graphql_schema;
1052//! # fn main() {}
1053//! # pub struct Context;
1054//! # impl juniper::Context for Context {}
1055//! # pub struct Article { id: ID }
1056//! # impl ArticleFields for Article {
1057//! # fn field_id(
1058//! # &self,
1059//! # executor: &Executor<'_, Context>,
1060//! # ) -> FieldResult<&ID> { unimplemented!() }
1061//! # }
1062//! # pub struct Tweet { id: ID, text: String }
1063//! # impl TweetFields for Tweet {
1064//! # fn field_id(
1065//! # &self,
1066//! # executor: &Executor<'_, Context>,
1067//! # ) -> FieldResult<&ID> { unimplemented!() }
1068//! # }
1069//! #
1070//! graphql_schema! {
1071//! schema {
1072//! query: Query
1073//! }
1074//!
1075//! type Query {
1076//! search(query: String!): [SearchResult!]!
1077//! }
1078//!
1079//! interface SearchResult {
1080//! id: ID!
1081//! }
1082//!
1083//! type Article implements SearchResult {
1084//! id: ID!
1085//! }
1086//!
1087//! type Tweet implements SearchResult {
1088//! id: ID!
1089//! }
1090//! }
1091//!
1092//! pub struct Query;
1093//!
1094//! impl QueryFields for Query {
1095//! fn field_search(
1096//! &self,
1097//! executor: &Executor<'_, Context>,
1098//! trail: &QueryTrail<'_, SearchResult, juniper_from_schema::Walked>,
1099//! query: String,
1100//! ) -> FieldResult<&Vec<SearchResult>> {
1101//! let article_trail: QueryTrail<'_, Article, Walked> = trail.downcast();
1102//! let tweet_trail: QueryTrail<'_, Tweet, Walked> = trail.downcast();
1103//!
1104//! // ...
1105//! # unimplemented!()
1106//! }
1107//! }
1108//! ```
1109//!
1110//! ### Why is this useful?
1111//!
1112//! If you were do perform some kind of preloading of data you might have a function that inspects
1113//! a `QueryTrail` and loads the necessary data from a database. Such a function could look like
1114//! this:
1115//!
1116//! ```ignore
1117//! fn preload_users(
1118//! mut users: Vec<User>,
1119//! query_trail: &QueryTrail<'_, User, Walked>,
1120//! db: &Database,
1121//! ) -> Vec<User> {
1122//! // ...
1123//! }
1124//! ```
1125//!
1126//! This function works well when we have field that returns `[User!]!`. That field is going to get
1127//! a `QueryTrail<'a, User, Walked>` which is exactly what `preload_users` needs.
1128//!
1129//! However, now imagine you have a schema like this:
1130//!
1131//! ```graphql
1132//! type Query {
1133//! search(query: String!): [SearchResult!]!
1134//! }
1135//!
1136//! union SearchResult = User | City | Country
1137//!
1138//! type User {
1139//! id: ID!
1140//! city: City!
1141//! }
1142//!
1143//! type City {
1144//! id: ID!
1145//! country: Country!
1146//! }
1147//!
1148//! type Country {
1149//! id: ID!
1150//! }
1151//! ```
1152//!
1153//! The method `QueryFields::field_search` will receive a `QueryTrail<'a, SearchResult, Walked>`.
1154//! That type doesn't work with `preload_users`. So we have to convert our `QueryTrail<'a,
1155//! SearchResult, Walked>` into `QueryTrail<'a, User, Walked>`.
1156//!
1157//! This can be done by calling `.downcast()` which automatically gets implemented for interface and
1158//! union query trails. See above for an example.
1159//!
1160//! ## `QueryTrail`s for fields that take arguments
1161//!
1162//! Sometimes you have GraphQL fields that take arguments that impact which things your resolvers
1163//! should return. `QueryTrail` therefore also allows you inspect arguments to fields.
1164//!
1165//! Abbreviated example:
1166//!
1167//! ```
1168//! # #[macro_use]
1169//! # extern crate juniper;
1170//! # use juniper_from_schema::*;
1171//! # pub struct Context;
1172//! # impl juniper::Context for Context {}
1173//! # fn main() {}
1174//! # pub struct Country {}
1175//! # impl CountryFields for Country {
1176//! # fn field_users<'a>(
1177//! # &self,
1178//! # executor: &juniper::Executor<'a, Context>,
1179//! # trail: &QueryTrail<'a, User, Walked>,
1180//! # active_since: DateTime<Utc>,
1181//! # ) -> juniper::FieldResult<Vec<User>> {
1182//! # unimplemented!()
1183//! # }
1184//! # }
1185//! # pub struct User {}
1186//! # impl UserFields for User {
1187//! # fn field_id<'a>(
1188//! # &self,
1189//! # executor: &juniper::Executor<'a, Context>,
1190//! # ) -> juniper::FieldResult<&juniper::ID> {
1191//! # unimplemented!()
1192//! # }
1193//! # }
1194//! use chrono::prelude::*;
1195//!
1196//! graphql_schema! {
1197//! schema {
1198//! query: Query
1199//! }
1200//!
1201//! type Query {
1202//! countries: [Country!]! @juniper(ownership: "owned")
1203//! }
1204//!
1205//! type Country {
1206//! users(activeSince: DateTimeUtc!): [User!]! @juniper(ownership: "owned")
1207//! }
1208//!
1209//! type User {
1210//! id: ID!
1211//! }
1212//!
1213//! scalar DateTimeUtc
1214//! }
1215//!
1216//! pub struct Query;
1217//!
1218//! impl QueryFields for Query {
1219//! fn field_countries<'a>(
1220//! &self,
1221//! executor: &'a juniper::Executor<'a, Context>,
1222//! trail: &'a QueryTrail<'a, Country, Walked>
1223//! ) -> juniper::FieldResult<Vec<Country>> {
1224//! // Get struct that has all arguments passed to `Country.users`
1225//! let args: CountryUsersArgs<'a> = trail.users_args();
1226//!
1227//! // The struct has methods for each argument, e.g. `active_since`.
1228//! //
1229//! // Notice that it automatically converts the incoming value to
1230//! // a `DateTime<Utc>`.
1231//! let _: DateTime<Utc> = args.active_since();
1232//!
1233//! # unimplemented!()
1234//! // ...
1235//! }
1236//! }
1237//! ```
1238//!
1239//! You can also elide the `'a` lifetime:
1240//!
1241//! ```
1242//! # #[macro_use]
1243//! # extern crate juniper;
1244//! # use juniper_from_schema::*;
1245//! # pub struct Context;
1246//! # impl juniper::Context for Context {}
1247//! # fn main() {}
1248//! # pub struct Country {}
1249//! # impl CountryFields for Country {
1250//! # fn field_users<'a>(
1251//! # &self,
1252//! # executor: &juniper::Executor<'a, Context>,
1253//! # trail: &QueryTrail<'a, User, Walked>,
1254//! # active_since: DateTime<Utc>,
1255//! # ) -> juniper::FieldResult<Vec<User>> {
1256//! # unimplemented!()
1257//! # }
1258//! # }
1259//! # pub struct User {}
1260//! # impl UserFields for User {
1261//! # fn field_id<'a>(
1262//! # &self,
1263//! # executor: &juniper::Executor<'a, Context>,
1264//! # ) -> juniper::FieldResult<&juniper::ID> {
1265//! # unimplemented!()
1266//! # }
1267//! # }
1268//! # use chrono::prelude::*;
1269//! # graphql_schema! {
1270//! # schema {
1271//! # query: Query
1272//! # }
1273//! # type Query {
1274//! # countries: [Country!]! @juniper(ownership: "owned")
1275//! # }
1276//! # type Country {
1277//! # users(activeSince: DateTimeUtc!): [User!]! @juniper(ownership: "owned")
1278//! # }
1279//! # type User {
1280//! # id: ID!
1281//! # }
1282//! # scalar DateTimeUtc
1283//! # }
1284//! # pub struct Query;
1285//! #
1286//! impl QueryFields for Query {
1287//! fn field_countries(
1288//! &self,
1289//! executor: &juniper::Executor<'_, Context>,
1290//! trail: &QueryTrail<'_, Country, Walked>
1291//! ) -> juniper::FieldResult<Vec<Country>> {
1292//! let args: CountryUsersArgs = trail.users_args();
1293//!
1294//! # unimplemented!()
1295//! // ...
1296//! }
1297//! }
1298//! ```
1299//!
1300//! The name of the arguments struct will always be `{name of type}{name of field}Args` (e.g.
1301//! `CountryUsersArgs`). The method names will always be the name of the arguments in snake case.
1302//!
1303//! The `*_args` method is only defined on `Walked` query trails so if you get an error like:
1304//!
1305//! ```text
1306//! ---- src/lib.rs - (line 10) stdout ----
1307//! error[E0599]: no method named `users_args` found for type `&QueryTrail<'_, Country, Walked>` in the current
1308//! scope
1309//! --> src/lib.rs:10:1
1310//! |
1311//! 10 | trail.users_args();
1312//! | ^^^^^^^^^^^^ method not found in `&QueryTrail<'_, Country, Walked>`
1313//! ```
1314//!
1315//! It is likely because you've forgotten to call [`.walk()`][] on `trail`.
1316//!
1317//! [`.walk()`]: struct.QueryTrail.html#method.walk
1318//!
1319//! Remember that you can always run `cargo doc` to get a high level overview of the generated
1320//! code.
1321//!
1322//! # Customizing the error type
1323//!
1324//! By default the return type of the generated field methods will be [`juniper::FieldResult<T>`].
1325//! That is just a type alias for `std::result::Result<T, juniper::FieldError>`. Should you want to
1326//! use a different error type than [`juniper::FieldError`] that can be done by passing `,
1327//! error_type: YourType` to [`graphql_schema_from_file!`].
1328//!
1329//! Just keep in that your custom error type must implement [`juniper::IntoFieldError`] to
1330//! type check.
1331//!
1332//! Example:
1333//!
1334//! ```
1335//! # #[macro_use]
1336//! # extern crate juniper;
1337//! # use juniper::*;
1338//! # use juniper_from_schema::graphql_schema_from_file;
1339//! # fn main() {}
1340//! # pub struct Context;
1341//! # impl juniper::Context for Context {}
1342//! # pub struct Mutation;
1343//! # impl MutationFields for Mutation {
1344//! # fn field_noop(&self, executor: &Executor<'_, Context>) -> Result<&bool, MyError> {
1345//! # Ok(&true)
1346//! # }
1347//! # }
1348//! graphql_schema_from_file!("tests/schemas/doc_schema.graphql", error_type: MyError);
1349//!
1350//! pub struct MyError(String);
1351//!
1352//! impl juniper::IntoFieldError for MyError {
1353//! fn into_field_error(self) -> juniper::FieldError {
1354//! // Perform custom error handling
1355//! juniper::FieldError::from(self.0)
1356//! }
1357//! }
1358//!
1359//! pub struct Query;
1360//!
1361//! impl QueryFields for Query {
1362//! fn field_hello_world(
1363//! &self,
1364//! executor: &Executor<'_, Context>,
1365//! name: String,
1366//! ) -> Result<String, MyError> {
1367//! Ok(format!("Hello, {}!", name))
1368//! }
1369//! }
1370//! ```
1371//!
1372//! [`graphql_schema!`] does not support changing the error type.
1373//!
1374//! [`graphql_schema!`]: macro.graphql_schema.html
1375//! [`graphql_schema_from_file!`]: macro.graphql_schema_from_file.html
1376//! [`juniper::IntoFieldError`]: https://docs.rs/juniper/0.11.1/juniper/trait.IntoFieldError.html
1377//! [`juniper::FieldError`]: https://docs.rs/juniper/0.11.1/juniper/struct.FieldError.html
1378//! [`juniper::FieldResult<T>`]: https://docs.rs/juniper/0.11.1/juniper/type.FieldResult.html
1379//!
1380//! # Customizing the context type
1381//!
1382//! By default the generate code will assume your context type is called `Context`. If that is not
1383//! the case you can customize it by calling [`graphql_schema_from_file!`] with `context_type: NewName`.
1384//!
1385//! Example:
1386//!
1387//! ```
1388//! # #[macro_use]
1389//! # extern crate juniper;
1390//! # use juniper::*;
1391//! # use juniper_from_schema::graphql_schema_from_file;
1392//! # fn main() {}
1393//! # pub struct Mutation;
1394//! # impl MutationFields for Mutation {
1395//! # fn field_noop(&self, executor: &Executor<'_, MyContext>) -> juniper::FieldResult<&bool> {
1396//! # Ok(&true)
1397//! # }
1398//! # }
1399//! graphql_schema_from_file!("tests/schemas/doc_schema.graphql", context_type: MyContext);
1400//!
1401//! pub struct MyContext;
1402//! impl juniper::Context for MyContext {}
1403//!
1404//! pub struct Query;
1405//!
1406//! impl QueryFields for Query {
1407//! fn field_hello_world(
1408//! &self,
1409//! executor: &Executor<'_, MyContext>,
1410//! name: String,
1411//! ) -> juniper::FieldResult<String> {
1412//! Ok(format!("Hello, {}!", name))
1413//! }
1414//! }
1415//! ```
1416//!
1417//! [`graphql_schema!`] does not support changing the context type.
1418//!
1419//! [`graphql_schema!`]: macro.graphql_schema.html
1420//! [`graphql_schema_from_file!`]: macro.graphql_schema_from_file.html
1421//!
1422//! # Inspecting the generated code
1423//!
1424//! If you wish to see exactly what code gets generated you can set the env var
1425//! `JUNIPER_FROM_SCHEMA_DEBUG` to `1` when compiling. For example:
1426//!
1427//! ```bash
1428//! JUNIPER_FROM_SCHEMA_DEBUG=1 cargo build
1429//! ```
1430//!
1431//! The code will not be formatted so it might be tricky to read. The easiest way to fix this is to
1432//! copy the printed code to a file and run it through [rustfmt].
1433//!
1434//! Alternatively you can include the [feature] called `"format-debug-output"`. This will run the
1435//! output through [rustfmt] before printing it. That way you don't have to do that manually.
1436//! Example `Cargo.toml`:
1437//!
1438//! ```toml
1439//! [dependencies]
1440//! juniper-from-schema = { version = "x.y.z", features = ["format-debug-output"] }
1441//! ```
1442//!
1443//! Unfortunately this requires that you're using nightly, because [rustfmt requires
1444//! nightly](https://github.com/rust-lang/rustfmt#installing-from-source). It might also break your
1445//! build because [rustfmt] doesn't always compile for some reason ¯\\\_(ツ)\_/¯. If you experience
1446//! this just remove the `"format-debug-output"` feature and format the output manually.
1447//!
1448//! [feature]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choosing-features
1449//! [rustfmt]: https://github.com/rust-lang/rustfmt
1450
1451#![deny(
1452 missing_docs,
1453 unused_imports,
1454 dead_code,
1455 unused_variables,
1456 unused_must_use
1457)]
1458#![doc(html_root_url = "https://docs.rs/juniper-from-schema/0.5.2")]
1459
1460use juniper::{DefaultScalarValue, LookAheadSelection};
1461use std::marker::PhantomData;
1462
1463pub use juniper_from_schema_code_gen::{graphql_schema, graphql_schema_from_file};
1464
1465/// A type used to parameterize `QueryTrail` to know that `walk` has been called.
1466pub struct Walked;
1467
1468/// A type used to parameterize `QueryTrail` to know that `walk` has *not* been called.
1469pub struct NotWalked;
1470
1471/// A wrapper around a `juniper::LookAheadSelection` with methods for each possible child.
1472pub struct QueryTrail<'a, T, K> {
1473 #[doc(hidden)]
1474 pub look_ahead: Option<&'a LookAheadSelection<'a, DefaultScalarValue>>,
1475 #[doc(hidden)]
1476 pub node_type: PhantomData<T>,
1477 #[doc(hidden)]
1478 pub walked: K,
1479}
1480
1481impl<'a, T> QueryTrail<'a, T, NotWalked> {
1482 /// Check if the trail is present in the query being executed
1483 pub fn walk(self) -> Option<QueryTrail<'a, T, Walked>> {
1484 match self.look_ahead {
1485 Some(inner) => Some(QueryTrail {
1486 look_ahead: Some(inner),
1487 node_type: self.node_type,
1488 walked: Walked,
1489 }),
1490 None => None,
1491 }
1492 }
1493}
1494
1495impl<'a, T, K> QueryTrail<'a, T, K> {
1496 #[allow(clippy::new_ret_no_self)]
1497 #[doc(hidden)]
1498 #[allow(missing_docs)]
1499 pub fn new(lh: &'a LookAheadSelection<'a, DefaultScalarValue>) -> QueryTrail<'a, T, Walked> {
1500 QueryTrail {
1501 look_ahead: Some(lh),
1502 node_type: PhantomData,
1503 walked: Walked,
1504 }
1505 }
1506}
1507
1508#[cfg(test)]
1509mod test {
1510 #[allow(unused_imports)]
1511 use super::*;
1512
1513 #[rustversion::nightly]
1514 #[test]
1515 fn test_compile_pass() {
1516 let t = trybuild::TestCases::new();
1517 t.pass("tests/compile_pass/*.rs");
1518 t.compile_fail("tests/compile_fail/*.rs");
1519 }
1520}