juniper_eager_loading/
lib.rs

1//! juniper-eager-loading is a library for avoiding N+1 query bugs designed to work with
2//! [Juniper][] and [juniper-from-schema][].
3//!
4//! It is designed to make the most common assocation setups easy to handle and while being
5//! flexible and allowing you to customize things as needed. It is also 100% data store agnostic.
6//! So regardless if your API is backed by an SQL database or another API you can still use this
7//! library.
8//!
9//! If you're familiar with N+1 queries in GraphQL and eager loading, feel free to skip forward to
10//! ["A real example"](#a-real-example).
11//!
12//! *NOTE*: Since this library requires [juniper-from-schema][] it is best if you're first familiar
13//! with that.
14//!
15//! # Table of contents
16//!
17//! - [What is N+1 query bugs?](#what-is-n1-query-bugs)
18//!     - [N+1s in GraphQL](#n1s-in-graphql)
19//! - [How this library works at a high level](#how-this-library-works-at-a-high-level)
20//! - [A real example](#a-real-example)
21//! - [`#[derive(EagerLoading)]`](#deriveeagerloading)
22//!     - [Attributes](#attributes)
23//! - [Associations](#associations)
24//!     - [Attributes supported on all associations](#attributes-supported-on-all-associations)
25//! - [Eager loading interfaces or unions](#eager-loading-interfaces-or-unions)
26//! - [Eager loading fields that take arguments](#eager-loading-fields-that-take-arguments)
27//! - [Diesel helper](#diesel-helper)
28//! - [When your GraphQL schema doesn't match your database schema](#when-your-graphql-schema-doesnt-match-your-database-schema)
29//!
30//! # What is N+1 query bugs?
31//!
32//! Imagine you have the following GraphQL schema
33//!
34//! ```graphql
35//! schema {
36//!     query: Query
37//! }
38//!
39//! type Query {
40//!     allUsers: [User!]!
41//! }
42//!
43//! type User {
44//!     id: Int!
45//!     country: Country!
46//! }
47//!
48//! type Country {
49//!     id: Int!
50//! }
51//! ```
52//!
53//! And someone executes the following query:
54//!
55//! ```graphql
56//! query SomeQuery {
57//!     allUsers {
58//!         country {
59//!             id
60//!         }
61//!     }
62//! }
63//! ```
64//!
65//! If you resolve that query naively with an SQL database as you data store you will see something
66//! like this in your logs:
67//!
68//! ```sql
69//! select * from users
70//! select * from countries where id = ?
71//! select * from countries where id = ?
72//! select * from countries where id = ?
73//! select * from countries where id = ?
74//! ...
75//! ```
76//!
77//! This happens because you first load all the users and then for each user in a loop you load
78//! that user's country. That is 1 query to load the users and N additional queries to load the
79//! countries. Therefore the name "N+1 query". These kinds of bugs can really hurt performance of
80//! your app since you're doing many more database calls than necessary.
81//!
82//! One possible solution to this is called "eager loading". The idea is to load all countries up
83//! front, before looping over the users. So instead of doing N+1 queries you do 2:
84//!
85//! ```sql
86//! select * from users
87//! select * from countries where id in (?, ?, ?, ?)
88//! ```
89//!
90//! Since you're loading the countries up front, this strategy is called "eager loading".
91//!
92//! ## N+1s in GraphQL
93//!
94//! If you're not careful when implementing a GraphQL API you'll have lots of these N+1 query bugs.
95//! Whenever a field returns a list of types and those types perform queries in their resolvers,
96//! you'll have N+1 query bugs.
97//!
98//! This is also a problem in REST APIs, however because the responses are fixed we can more easily
99//! setup the necessary eager loads because we know the types needed to compute the response.
100//!
101//! However in GraphQL the responses are not fixed. They depend on the incoming queries, which are
102//! not known ahead of time. So setting up the correct amount of eager loading requires inspecting
103//! the queries before executing them and eager loading the types requested such that the actual
104//! resolvers wont need to run queries. That is exactly what this library does.
105//!
106//! # How this library works at a high level
107//!
108//! If you have a GraphQL type like this
109//!
110//! ```graphql
111//! type User {
112//!     id: Int!
113//!     country: Country!
114//! }
115//! ```
116//!
117//! You might create the corresponding Rust model type like this:
118//!
119//! ```
120//! struct User {
121//!     id: i32,
122//!     country_id: i32
123//! }
124//! ```
125//!
126//! However this approach has one big issue. How are you going to resolve the field `User.country`
127//! without doing a database query? All the resolver has access to is a `User` with a `country_id`
128//! field. It can't get the country without loading it from the database...
129//!
130//! Fundamentally these kinds of model structs don't work for eager loading with GraphQL. So
131//! this library takes a different approach.
132//!
133//! What if we created separate structs for the database models and the GraphQL models? Something
134//! like this:
135//!
136//! ```
137//! # fn main() {}
138//! #
139//! mod models {
140//!     pub struct User {
141//!         id: i32,
142//!         country_id: i32
143//!     }
144//!
145//!     pub struct Country {
146//!         id: i32,
147//!     }
148//! }
149//!
150//! struct User {
151//!     user: models::User,
152//!     country: HasOne<Country>,
153//! }
154//!
155//! struct Country {
156//!     country: models::Country
157//! }
158//!
159//! enum HasOne<T> {
160//!     Loaded(T),
161//!     NotLoaded,
162//! }
163//! ```
164//!
165//! Now we're able to resolve the query with code like this:
166//!
167//! 1. Load all the users (first query).
168//! 2. Map the users to a list of country ids.
169//! 3. Load all the countries with those ids (second query).
170//! 4. Pair up the users with the country with the correct id, so change `User.country` from
171//!    `HasOne::NotLoaded` to `HasOne::Loaded(matching_country)`.
172//! 5. When resolving the GraphQL field `User.country` simply return the loaded country.
173//!
174//! # A real example
175//!
176//! ```
177//! use juniper::{Executor, FieldResult};
178//! use juniper_eager_loading::{prelude::*, EagerLoading, HasOne};
179//! use juniper_from_schema::graphql_schema;
180//! use std::error::Error;
181//!
182//! // Define our GraphQL schema.
183//! graphql_schema! {
184//!     schema {
185//!         query: Query
186//!     }
187//!
188//!     type Query {
189//!         allUsers: [User!]! @juniper(ownership: "owned")
190//!     }
191//!
192//!     type User {
193//!         id: Int!
194//!         country: Country!
195//!     }
196//!
197//!     type Country {
198//!         id: Int!
199//!     }
200//! }
201//!
202//! // Our model types.
203//! mod models {
204//!     use std::error::Error;
205//!     use juniper_eager_loading::LoadFrom;
206//!
207//!     #[derive(Clone)]
208//!     pub struct User {
209//!         pub id: i32,
210//!         pub country_id: i32
211//!     }
212//!
213//!     #[derive(Clone)]
214//!     pub struct Country {
215//!         pub id: i32,
216//!     }
217//!
218//!     // This trait is required for eager loading countries.
219//!     // It defines how to load a list of countries from a list of ids.
220//!     // Notice that `Context` is generic and can be whatever you want.
221//!     // It will normally be your Juniper context which would contain
222//!     // a database connection.
223//!     impl LoadFrom<i32> for Country {
224//!         type Error = Box<dyn Error>;
225//!         type Context = super::Context;
226//!
227//!         fn load(
228//!             employments: &[i32],
229//!             field_args: &(),
230//!             ctx: &Self::Context,
231//!         ) -> Result<Vec<Self>, Self::Error> {
232//!             // ...
233//!             # unimplemented!()
234//!         }
235//!     }
236//! }
237//!
238//! // Our sample database connection type.
239//! pub struct DbConnection;
240//!
241//! impl DbConnection {
242//!     // Function that will load all the users.
243//!     fn load_all_users(&self) -> Vec<models::User> {
244//!         // ...
245//!         # unimplemented!()
246//!     }
247//! }
248//!
249//! // Our Juniper context type which contains a database connection.
250//! pub struct Context {
251//!     db: DbConnection,
252//! }
253//!
254//! impl juniper::Context for Context {}
255//!
256//! // Our GraphQL user type.
257//! // `#[derive(EagerLoading)]` takes care of generating all the boilerplate code.
258//! #[derive(Clone, EagerLoading)]
259//! // You need to set the context and error type.
260//! #[eager_loading(
261//!     context = Context,
262//!     error = Box<dyn Error>,
263//!
264//!     // These match the default so you wouldn't have to specify them
265//!     model = models::User,
266//!     id = i32,
267//!     root_model_field = user,
268//! )]
269//! pub struct User {
270//!     // This user model is used to resolve `User.id`
271//!     user: models::User,
272//!
273//!     // Setup a "has one" association between a user and a country.
274//!     //
275//!     // We could also have used `#[has_one(default)]` here.
276//!     #[has_one(
277//!         foreign_key_field = country_id,
278//!         root_model_field = country,
279//!         graphql_field = country,
280//!     )]
281//!     country: HasOne<Country>,
282//! }
283//!
284//! // And the GraphQL country type.
285//! #[derive(Clone, EagerLoading)]
286//! #[eager_loading(context = Context, error = Box<dyn Error>)]
287//! pub struct Country {
288//!     country: models::Country,
289//! }
290//!
291//! // The root query GraphQL type.
292//! pub struct Query;
293//!
294//! impl QueryFields for Query {
295//!     // The resolver for `Query.allUsers`.
296//!     fn field_all_users(
297//!         &self,
298//!         executor: &Executor<'_, Context>,
299//!         trail: &QueryTrail<'_, User, Walked>,
300//!     ) -> FieldResult<Vec<User>> {
301//!         let ctx = executor.context();
302//!
303//!         // Load the model users.
304//!         let user_models = ctx.db.load_all_users();
305//!
306//!         // Turn the model users into GraphQL users.
307//!         let mut users = User::from_db_models(&user_models);
308//!
309//!         // Perform the eager loading.
310//!         // `trail` is used to only eager load the fields that are requested. Because
311//!         // we're using `QueryTrail`s from "juniper_from_schema" it would be a compile
312//!         // error if we eager loaded associations that aren't requested in the query.
313//!         User::eager_load_all_children_for_each(&mut users, &user_models, ctx, trail)?;
314//!
315//!         Ok(users)
316//!     }
317//! }
318//!
319//! impl UserFields for User {
320//!     fn field_id(
321//!         &self,
322//!         executor: &Executor<'_, Context>,
323//!     ) -> FieldResult<&i32> {
324//!         Ok(&self.user.id)
325//!     }
326//!
327//!     fn field_country(
328//!         &self,
329//!         executor: &Executor<'_, Context>,
330//!         trail: &QueryTrail<'_, Country, Walked>,
331//!     ) -> FieldResult<&Country> {
332//!         // This will unwrap the country from the `HasOne` or return an error if the
333//!         // country wasn't loaded, or wasn't found in the database.
334//!         Ok(self.country.try_unwrap()?)
335//!     }
336//! }
337//!
338//! impl CountryFields for Country {
339//!     fn field_id(
340//!         &self,
341//!         executor: &Executor<'_, Context>,
342//!     ) -> FieldResult<&i32> {
343//!         Ok(&self.country.id)
344//!     }
345//! }
346//! #
347//! # fn main() {}
348//! ```
349//!
350//! # `#[derive(EagerLoading)]`
351//!
352//! For a type to support eager loading it needs to implement the following traits:
353//!
354//! - [`GraphqlNodeForModel`][]
355//! - [`EagerLoadAllChildren`][]
356//! - Each association field must implement [`EagerLoadChildrenOfType`][]
357//!
358//! [`GraphqlNodeForModel`]: trait.GraphqlNodeForModel.html
359//! [`EagerLoadAllChildren`]: trait.EagerLoadAllChildren.html
360//!
361//! Implementing these traits involves lots of boilerplate, therefore you should use
362//! `#[derive(EagerLoading)]` to derive implementations as much as possible.
363//!
364//! Sometimes you might need customized eager loading for a specific association, in that case you
365//! should still have `#[derive(EagerLoading)]` on your struct but implement
366//! [`EagerLoadChildrenOfType`][] yourself for the field that requires a custom setup. An example
367//! of how to do that can be found
368//! [here](trait.EagerLoadChildrenOfType.html#manual-implementation).
369//!
370//! If you're interested in seeing full examples without any macros look
371//! [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples).
372//!
373//! [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
374//!
375//! ## Attributes
376//!
377//! `#[derive(EagerLoading)]` has a few attributes you need to provide:
378//!
379//! | Name | Description | Default | Example |
380//! |---|---|---|---|
381//! | `context` | The type of your Juniper context. This will often hold your database connection or something else than can be used to load data. | N/A | `context = Context` |
382//! | `error` | The type of error eager loading might result in. | N/A | `error = diesel::result::Error` |
383//! | `model` | The model type behind your GraphQL struct | `models::{name of struct}` | `model = crate::db::models::User` |
384//! | `id` | Which id type does your app use? | `i32` | `id = UUID` |
385//! | `root_model_field` | The name of the field has holds the backing model | `{name of struct}` in snakecase. | `root_model_field = user` |
386//! | `primary_key_field` | The field that holds the primary key of the model. This field is only used by code generated for `#[has_many]` and `#[has_many_through]` associations. | `id` | `primary_key_field = identifier` |
387//! | `print` | If set it will print the generated implementation of `GraphqlNodeForModel` and `EagerLoadAllChildren` | Not set | `print` |
388//!
389//! # Associations
390//!
391//! Assocations are things like "user has one country". These are the fields that need to be eager
392//! loaded to avoid N+1s. Each assocation works for different kinds of foreign key setups and has
393//! to be eager loaded differently. They should fit most kinds of associations you have in your
394//! app. Click on each for more detail.
395//!
396//! The documation for each assocation assumes that you're using an SQL database, but it should be
397//! straight forward to adapt to other kinds of data stores.
398//!
399//! - [`HasOne`](struct.HasOne.html)
400//! - [`OptionHasOne`](struct.OptionHasOne.html)
401//! - [`HasMany`](struct.HasMany.html)
402//! - [`HasManyThrough`](struct.HasManyThrough.html)
403//!
404//! For each field of your GraphQL struct that is one of these four types the trait
405//! [`EagerLoadChildrenOfType`][] will be implemented by `#[derive(EagerLoading)]`.
406//!
407//! ## Attributes supported on all associations
408//!
409//! These are the attributes that are supported on all associations.
410//!
411//! ### `skip`
412//!
413//! Skip implementing [`EagerLoadChildrenOfType`][] for the field. This is useful if you need to
414//! provide a custom implementation.
415//!
416//! ### `print`
417//!
418//! This will cause the implementation of [`EagerLoadChildrenOfType`][] for the field to be printed
419//! while compiling. This is useful when combined with `skip`. It will print a good starting place
420//! for you to customize.
421//!
422//! The resulting code wont be formatted. We recommend you do that with
423//! [rustfmt](https://github.com/rust-lang/rustfmt).
424//!
425//! ### `fields_arguments`
426//!
427//! Used to specify the type that'll be use for [`EagerLoadChildrenOfType::FieldArguments`][]. More
428//! info [here](#eager-loading-fields-that-take-arguments).
429//!
430//! For example `#[has_one(fields_arguments = CountryUsersArgs)]`. You can find a complete example
431//! [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples/field_with_arguments.rs).
432//!
433//! The code generation defaults [`EagerLoadChildrenOfType::FieldArguments`][] to `()`. That works
434//! for fields that don't take arguments.
435//!
436//! [`EagerLoadChildrenOfType::FieldArguments`]: trait.EagerLoadChildrenOfType.html#associatedtype.FieldArguments
437//!
438//! # Eager loading interfaces or unions
439//!
440//! Eager loading interfaces or unions is possible but it will require calling `.downcast()` on the
441//! `QueryTrail`. See the [juniper-from-schema docs for more
442//! info](https://docs.rs/juniper-from-schema/0.4.0/juniper_from_schema/#downcasting-for-interface-and-union-querytrails)
443//! fo more info.
444//!
445//! # Eager loading fields that take arguments
446//!
447//! If you have a GraphQL field that takes arguments you probably have to consider them for eager
448//! loading purposes.
449//!
450//! If you're using on code generation for such fields you have to specify the type on the
451//! association field. More into [here](/#fields_arguments).
452//!
453//! If you implement [`EagerLoadChildrenOfType`][] manually you have to set
454//! [`EagerLoadChildrenOfType::FieldArguments`][] to the type of the arguments struct generated by
455//! juniper-from-schema. You can find more info
456//! [here](https://docs.rs/juniper-from-schema/0.5.0/juniper_from_schema/#querytrails-for-fields-that-take-arguments).
457//!
458//! You also have to implement [`LoadFrom<T, ArgumentType>`][`LoadFrom`] for your model. You can find a complete
459//! example
460//! [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples/field_with_arguments.rs).
461//!
462//! If you see a type error like:
463//!
464//! ```text
465//! error[E0308]: mismatched types
466//!    --> src/main.rs:254:56
467//!     |
468//!    254 | #[derive(Clone, Eq, PartialEq, Debug, Ord, PartialOrd, EagerLoading)]
469//!     |                                                           ^^^^^^^^^^^^ expected (), found struct `query_trails::CountryUsersArgs`
470//!     |
471//!     = note: expected type `&()`
472//!                found type `&query_trails::CountryUsersArgs<'_>`
473//! ```
474//!
475//! It is because your GraphQL field `Country.users` takes arguments. The code generation
476//! defaults to using `()` for the type of the arguments so therefore you get this type error. The
477//! neat bit is that the compiler wont let you forget about handling arguments.
478//!
479//! [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
480//! [`EagerLoadChildrenOfType::FieldArguments`]: trait.EagerLoadChildrenOfType.html#associatedtype.FieldArguments
481//! [`LoadFrom`]: trait.LoadFrom.html
482//!
483//! # Diesel helper
484//!
485//! Implementing [`LoadFrom`][] for lots of model types might involve lots of boilerplate. If
486//! you're using Diesel it is recommend that you use one of [the macros to
487//! generate](index.html#macros) implementations.
488//!
489//! [`LoadFrom`]: trait.LoadFrom.html
490//! [Diesel]: https://diesel.rs
491//! [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
492//!
493//! # When your GraphQL schema doesn't match your database schema
494//!
495//! This library supports eager loading most kinds of association setups, however it probably
496//! doesn't support all that might exist in your app. It also works best when your database schema
497//! closely matches your GraphQL schema.
498//!
499//! If you find yourself having to implement something that isn't directly supported remember that
500//! you're still free to implement you resolver functions exactly as you want. So if doing queries
501//! in a resolver is the only way to get the behaviour you need then so be it. Avoiding some N+1
502//! queries is better than avoiding none.
503//!
504//! However if you have a setup that you think this library should support please don't hestitate
505//! to [open an issue](https://github.com/davidpdrsn/juniper-eager-loading).
506//!
507//! [Juniper]: https://github.com/graphql-rust/juniper
508//! [juniper-from-schema]: https://github.com/davidpdrsn/juniper-from-schema
509
510#![doc(html_root_url = "https://docs.rs/juniper-eager-loading/0.5.1")]
511#![allow(clippy::single_match, clippy::type_complexity)]
512#![deny(
513    missing_docs,
514    dead_code,
515    missing_copy_implementations,
516    missing_debug_implementations,
517    trivial_casts,
518    trivial_numeric_casts,
519    unsafe_code,
520    unstable_features,
521    unused_import_braces,
522    unused_imports,
523    unused_must_use,
524    unused_qualifications,
525    unused_variables
526)]
527
528mod association;
529mod macros;
530
531use juniper_from_schema::{QueryTrail, Walked};
532use std::{fmt, hash::Hash, mem::transmute_copy};
533
534pub use association::Association;
535pub use juniper_eager_loading_code_gen::EagerLoading;
536
537#[doc(hidden)]
538pub mod proc_macros {
539    pub use juniper_eager_loading_code_gen::{
540        impl_load_from_for_diesel_mysql, impl_load_from_for_diesel_pg,
541        impl_load_from_for_diesel_sqlite,
542    };
543}
544
545/// Re-exports the traits needed for doing eager loading. Meant to be glob imported.
546pub mod prelude {
547    pub use super::Association;
548    pub use super::EagerLoadAllChildren;
549    pub use super::EagerLoadChildrenOfType;
550    pub use super::GraphqlNodeForModel;
551}
552
553/// The types of associations.
554///
555/// This is used for [`Error`] to report which kind of association encountered an error.
556///
557/// [`Error`]: enum.Error.html
558#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
559pub enum AssociationType {
560    /// There was an error with a [`HasOne`](struct.HasOne.html).
561    HasOne,
562    /// There was an error with an [`OptionHasOne`](struct.OptionHasOne.html).
563    OptionHasOne,
564    /// There was an error with a [`HasMany`](struct.HasMany.html).
565    HasMany,
566    /// There was an error with a [`HasManyThrough`](struct.HasManyThrough.html).
567    HasManyThrough,
568}
569
570/// A non-optional "has one" association.
571///
572/// Imagine you have these models:
573///
574/// ```
575/// struct User {
576///     id: i32,
577///     country_id: i32,
578/// }
579///
580/// struct Country {
581///     id: i32,
582/// }
583/// ```
584///
585/// For this setup we say "a user has one country". This means that `User` has a field named
586/// `country_id` that references the id of another country.
587///
588/// # Example
589///
590/// You can find a complete example of `HasOne` [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples/has_one.rs).
591///
592/// # Attributes
593///
594/// | Name | Description | Default | Example |
595/// |---|---|---|---|
596/// | `foreign_key_field` | The name of the foreign key field | `{name of field}_id` | `foreign_key_field = country_id` |
597/// | `root_model_field` | The name of the field on the associated GraphQL type that holds the model | `{name of field}` | `root_model_field = country` |
598/// | `graphql_field` | The name of this field in your GraphQL schema | `{name of field}` | `graphql_field = country` |
599/// | `child_primary_key_field` | The name of the primary key field on the associated model | `id` | `child_primary_key_field = identifier` |
600/// | `default` | Use the default value for all unspecified attributes | N/A | `default` |
601///
602/// Additionally it also supports the attributes `print`, `skip`, and `field_arguments`. See the [root model
603/// docs](/#attributes-supported-on-all-associations) for more into on those.
604///
605/// # Errors
606///
607/// When calling [`try_unwrap`][] to get the loaded value it will return an error if the value has
608/// not been loaded, or if the load failed.
609///
610/// For example if a user has a `country_id` of `10` but there is no `Country` with id `10` then
611/// [`try_unwrap`][] will return an error.
612///
613/// [`try_unwrap`]: struct.HasOne.html#method.try_unwrap
614#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
615pub struct HasOne<T>(HasOneInner<T>);
616
617impl<T> Default for HasOne<T> {
618    fn default() -> Self {
619        HasOne(HasOneInner::default())
620    }
621}
622
623impl<T> HasOne<T> {
624    /// Borrow the loaded value. If the value has not been loaded it will return an error.
625    pub fn try_unwrap(&self) -> Result<&T, Error> {
626        self.0.try_unwrap()
627    }
628}
629
630#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
631enum HasOneInner<T> {
632    Loaded(T),
633    NotLoaded,
634    LoadFailed,
635}
636
637impl<T> Default for HasOneInner<T> {
638    fn default() -> Self {
639        HasOneInner::NotLoaded
640    }
641}
642
643impl<T> HasOneInner<T> {
644    fn try_unwrap(&self) -> Result<&T, Error> {
645        match self {
646            HasOneInner::Loaded(inner) => Ok(inner),
647            HasOneInner::NotLoaded => Err(Error::NotLoaded(AssociationType::HasOne)),
648            HasOneInner::LoadFailed => Err(Error::LoadFailed(AssociationType::HasOne)),
649        }
650    }
651
652    fn assert_loaded_otherwise_failed(&mut self) {
653        match self {
654            HasOneInner::NotLoaded => {
655                std::mem::replace(self, HasOneInner::LoadFailed);
656            }
657            _ => {}
658        }
659    }
660}
661
662/// An optional "has-one association".
663///
664/// It works exactly like [`HasOne`] except it doesn't error if the association doesn't get loaded.
665/// The value doesn't get loaded it defaults to `None`.
666///
667/// # Example
668///
669/// You can find a complete example of `OptionHasMany` [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples/option_has_one.rs).
670///
671/// # Attributes
672///
673/// It supports the same attributes as [`HasOne`].
674///
675/// [`HasOne`]: struct.HasOne.html
676///
677/// # Errors
678///
679/// [`try_unwrap`][] will never error. If the association wasn't loaded or wasn't found it will
680/// return `Ok(None)`.
681///
682/// [`try_unwrap`]: struct.OptionHasOne.html#method.try_unwrap
683#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
684pub struct OptionHasOne<T>(Option<T>);
685
686impl<T> Default for OptionHasOne<T> {
687    fn default() -> Self {
688        OptionHasOne(None)
689    }
690}
691
692impl<T> OptionHasOne<T> {
693    /// Borrow the loaded value. If the value has not been loaded it will return `Ok(None)`. It
694    /// will not error.
695    pub fn try_unwrap(&self) -> Result<&Option<T>, Error> {
696        Ok(&self.0)
697    }
698}
699
700/// A "has many" association.
701///
702/// Imagine you have these models:
703///
704/// ```
705/// struct User {
706///     id: i32,
707/// }
708///
709/// struct Car {
710///     id: i32,
711///     user_id: i32,
712/// }
713/// ```
714///
715/// For this setup we say "user has many cars" and "cars have one user". This is the inverse of a
716/// `HasOne` assocation because the foreign key is on `Car` instead of `User`.
717///
718/// This means users can own many cars, but cars can only be owned by one user.
719///
720/// # Example
721///
722/// You can find a complete example of `HasMany` [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples/has_many.rs).
723///
724/// # Attributes
725///
726/// | Name | Description | Default | Example |
727/// |---|---|---|---|
728/// | `foreign_key_field` | The name of the foreign key field | `{name of struct}_id` | `foreign_key_field = user_id` |
729/// | `foreign_key_optional` | The foreign key type is optional | Not set | `foreign_key_optional` |
730/// | `root_model_field` | The name of the field on the associated GraphQL type that holds the database model | N/A (unless using `skip`) | `root_model_field = car` |
731/// | `graphql_field` | The name of this field in your GraphQL schema | `{name of field}` | `graphql_field = country` |
732/// | `predicate_method` | Method used to filter child associations. This can be used if you only want to include a subset of the models | N/A (attribute is optional) | `predicate_method = a_predicate_method` |
733///
734/// Additionally it also supports the attributes `print`, `skip`, and `field_arguments`. See the [root model
735/// docs](/#attributes-supported-on-all-associations) for more into on those.
736///
737/// # Errors
738///
739/// [`try_unwrap`][] will never error. If the association wasn't loaded or wasn't found it will
740/// return `Ok(vec![])`.
741///
742/// [`try_unwrap`]: struct.HasMany.html#method.try_unwrap
743#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
744pub struct HasMany<T>(Vec<T>);
745
746impl<T> Default for HasMany<T> {
747    fn default() -> Self {
748        HasMany(Vec::new())
749    }
750}
751
752impl<T> HasMany<T> {
753    /// Borrow the loaded values. If no values have been loaded it will return an empty list.
754    /// It will not return an error.
755    pub fn try_unwrap(&self) -> Result<&Vec<T>, Error> {
756        Ok(&self.0)
757    }
758}
759
760/// A "has many through" association.
761///
762/// Imagine you have these models:
763///
764/// ```
765/// struct User {
766///     id: i32,
767/// }
768///
769/// struct Company {
770///     id: i32,
771/// }
772///
773/// struct Employments {
774///     id: i32,
775///     user_id: i32,
776///     company_id: i32,
777/// }
778/// ```
779///
780/// For this setup we say "user has many companies through employments". This means uses can work
781/// at many companies and companies can have many employees, provided that we join with `Employment`.
782///
783/// This requires that we use [the `JoinModel`](trait.EagerLoadChildrenOfType.html#joinmodel) type
784/// on [`EagerLoadChildrenOfType`][] and is therefore a bit different from the other associations
785/// since it involves a third type.
786///
787/// [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
788///
789/// # Example
790///
791/// You can find a complete example of `HasManyThrough` [here](https://github.com/davidpdrsn/juniper-eager-loading/tree/master/examples/has_many_through.rs).
792///
793/// # Attributes
794///
795/// | Name | Description | Default | Example |
796/// |---|---|---|---|
797/// | `model_field` | The field on the contained type that holds the model | `{name of contained type}` in snakecase | `model_field = company` |
798/// | `join_model` | The model we have to join with | N/A | `join_model = models::Employment` |
799/// | `child_primary_key_field_on_join_model` | The field on the join model that holds the primary key of the child model (`Company` in the example above) | `{name of model}_id` | `child_primary_key_field_on_join_model = company_identifier` |
800/// | `foreign_key_field` | The field on the join model that holds the primary key of the parent model (`User` in the example above) | `{name of model}_id` | `foreign_key_field = user_identifier` |
801/// | `child_primary_key_field` | The field on the child model that holds its primary key | `id` | `foreign_key_field = identifier` |
802/// | `graphql_field` | The name of this field in your GraphQL schema | `{name of field}` | `graphql_field = country` |
803/// | `predicate_method` | Method used to filter child associations. This can be used if you only want to include a subset of the models. This method will be called to filter the join models. | N/A (attribute is optional) | `predicate_method = a_predicate_method` |
804///
805/// Additionally it also supports the attributes `print`, `skip`, and `field_arguments`. See the [root model
806/// docs](/#attributes-supported-on-all-associations) for more into on those.
807///
808/// # Errors
809///
810/// [`try_unwrap`][] will never error. If the association wasn't loaded or wasn't found it will
811/// return `Ok(vec![])`.
812///
813/// [`try_unwrap`]: struct.HasManyThrough.html#method.try_unwrap
814#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
815pub struct HasManyThrough<T>(Vec<T>);
816
817impl<T> Default for HasManyThrough<T> {
818    fn default() -> Self {
819        HasManyThrough(Vec::new())
820    }
821}
822
823impl<T> HasManyThrough<T> {
824    /// Borrow the loaded values. If no values have been loaded it will return an empty list.
825    /// It will not return an error.
826    pub fn try_unwrap(&self) -> Result<&Vec<T>, Error> {
827        Ok(&self.0)
828    }
829}
830
831/// A GraphQL type backed by a model object.
832///
833/// You shouldn't need to implement this trait yourself even when customizing eager loading.
834pub trait GraphqlNodeForModel: Sized {
835    /// The model type.
836    type Model: Clone;
837
838    /// The id type the model uses.
839    type Id: 'static + Hash + Eq;
840
841    /// Your Juniper context type.
842    ///
843    /// This will typically contain a database connection or a connection to some external API.
844    type Context;
845
846    /// The error type.
847    type Error;
848
849    /// Create a new GraphQL type from a model.
850    fn new_from_model(model: &Self::Model) -> Self;
851
852    /// Create a list of GraphQL types from a list of models.
853    fn from_db_models(models: &[Self::Model]) -> Vec<Self> {
854        models
855            .iter()
856            .map(|model| Self::new_from_model(model))
857            .collect()
858    }
859}
860
861/// Perform eager loading for a single association of a GraphQL struct.
862///
863/// `#[derive(EagerLoading)]` will implement this trait for each [association field][] your GraphQL
864/// struct has.
865///
866/// [association field]: /#associations
867///
868/// # Manual implementation
869///
870/// Sometimes you might have a setup that `#[derive(EagerLoading)]` doesn't support. In those cases
871/// you have to implement this trait yourself for those struct fields. Here is an example of how to
872/// do that:
873///
874/// ```
875/// # use juniper::{Executor, FieldResult};
876/// # use juniper_eager_loading::{prelude::*, *};
877/// # use juniper_from_schema::graphql_schema;
878/// # use std::error::Error;
879/// # pub struct Query;
880/// # impl QueryFields for Query {
881/// #     fn field_noop(&self, executor: &Executor<'_, Context>) -> FieldResult<bool> {
882/// #         unimplemented!()
883/// #     }
884/// # }
885/// # impl juniper_eager_loading::LoadFrom<i32> for models::Country {
886/// #     type Error = Box<dyn std::error::Error>;
887/// #     type Context = Context;
888/// #     fn load(employments: &[i32], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
889/// #         unimplemented!()
890/// #     }
891/// # }
892/// # pub struct DbConnection;
893/// # impl DbConnection {
894/// #     fn load_all_users(&self) -> Vec<models::User> {
895/// #         unimplemented!()
896/// #     }
897/// # }
898/// # pub struct Context {
899/// #     db: DbConnection,
900/// # }
901/// # impl juniper::Context for Context {}
902/// # impl UserFields for User {
903/// #     fn field_id(&self, executor: &Executor<'_, Context>) -> FieldResult<&i32> {
904/// #         unimplemented!()
905/// #     }
906/// #     fn field_country(
907/// #         &self,
908/// #         executor: &Executor<'_, Context>,
909/// #         trail: &QueryTrail<'_, Country, Walked>,
910/// #     ) -> FieldResult<&Option<Country>> {
911/// #         unimplemented!()
912/// #     }
913/// # }
914/// # impl CountryFields for Country {
915/// #     fn field_id(&self, executor: &Executor<'_, Context>) -> FieldResult<&i32> {
916/// #         unimplemented!()
917/// #     }
918/// # }
919/// # fn main() {}
920/// #
921/// # graphql_schema! {
922/// #     schema { query: Query }
923/// #     type Query { noop: Boolean! @juniper(ownership: "owned") }
924/// #     type User {
925/// #         id: Int!
926/// #         country: Country
927/// #     }
928/// #     type Country {
929/// #         id: Int!
930/// #     }
931/// # }
932/// # mod models {
933/// #     #[derive(Clone)]
934/// #     pub struct User {
935/// #         pub id: i32,
936/// #         pub country_id: Option<i32>,
937/// #     }
938/// #     #[derive(Clone)]
939/// #     pub struct Country {
940/// #         pub id: i32,
941/// #     }
942/// # }
943/// #
944/// #[derive(Clone, EagerLoading)]
945/// #[eager_loading(context = Context, error = Box<dyn std::error::Error>)]
946/// pub struct User {
947///     user: models::User,
948///
949///     // Add `#[option_has_one(default, print)]` to get a good starting point for your
950///     // manual implementaion.
951///     #[option_has_one(skip)]
952///     country: OptionHasOne<Country>,
953/// }
954///
955/// #[derive(Clone, EagerLoading)]
956/// #[eager_loading(context = Context, error = Box<dyn std::error::Error>)]
957/// pub struct Country {
958///     country: models::Country,
959/// }
960///
961/// #[allow(missing_docs, dead_code)]
962/// struct EagerLoadingContextUserForCountry;
963///
964/// impl<'a>
965///     EagerLoadChildrenOfType<
966///         'a,
967///         Country,
968///         EagerLoadingContextUserForCountry,
969///     > for User
970/// {
971///     type FieldArguments = ();
972///
973///     fn load_children(
974///         models: &[Self::Model],
975///         field_args: &Self::FieldArguments,
976///         ctx: &Self::Context,
977///     ) -> Result<
978///         LoadChildrenOutput<<Country as juniper_eager_loading::GraphqlNodeForModel>::Model>,
979///         Self::Error,
980///     > {
981///         let ids = models
982///             .iter()
983///             .filter_map(|model| model.country_id)
984///             .map(|id| id.clone())
985///             .collect::<Vec<_>>();
986///         let ids = juniper_eager_loading::unique(ids);
987///
988///         let children = <
989///             <Country as GraphqlNodeForModel>::Model as juniper_eager_loading::LoadFrom<Self::Id>
990///         >::load(&ids, field_args, ctx)?;
991///
992///         Ok(juniper_eager_loading::LoadChildrenOutput::ChildModels(children))
993///     }
994///
995///     fn is_child_of(
996///         node: &Self,
997///         child: &Country,
998///         _join_model: &(), _field_args: &Self::FieldArguments,
999///         _ctx: &Self::Context,
1000///     ) -> bool {
1001///         node.user.country_id == Some(child.country.id)
1002///     }
1003///
1004///     fn association(node: &mut Self) -> &mut dyn Association<Country> {
1005///         &mut node.country
1006///     }
1007/// }
1008/// ```
1009///
1010/// # Generic parameters
1011///
1012/// The number of generic parameters to this trait might look scary, but in the vast majority of
1013/// cases you shouldn't have to worry about them.
1014///
1015/// ## `Child`
1016///
1017/// Is the model type of the child. If your `User` struct has a field of type `OptionHasOne<Country>`,
1018/// this type will default to `models::Country`.
1019///
1020/// ## `ImplContext`
1021///
1022/// This "context" type is needed in case your GraphQL type has multiple assocations to values
1023/// of the same type. Could for example be something like this
1024///
1025/// ```ignore
1026/// struct User {
1027///     home_country: HasOne<Country>,
1028///     current_country: HasOne<Country>,
1029/// }
1030/// ```
1031///
1032/// If we didn't have this we wouldn't be able to implement `EagerLoadChildrenOfType<Country>`
1033/// twice for `User`, because you cannot implement the same trait twice for the same type.
1034///
1035/// Note that this is _not_ the Juniper GraphQL context.
1036///
1037/// ## `JoinModel`
1038///
1039/// This type defaults to `()` and is only need for [`HasManyThrough`][]. In the other associations
1040/// there are only two types involved (such as `models::User` and `models::Country`) and one of
1041/// them will have a foreign key pointing to the other one. But consider this scenario instead
1042/// where users can work for many companies, and companies can have many employees:
1043///
1044/// ```
1045/// mod models {
1046///     struct User {
1047///         id: i32,
1048///     }
1049///
1050///     struct Company {
1051///         id: i32,
1052///     }
1053///
1054///     struct Employment {
1055///         id: i32,
1056///         user_id: i32,
1057///         company_id: i32,
1058///     }
1059/// }
1060/// ```
1061///
1062/// Imagine now we need to eager load the list of companies a given user works at. That means
1063/// [`LoadFrom`][] would return `Vec<models::Company>`. However that isn't enough information once
1064/// we need to pair users up with the correct companies. `User` doesn't have `company_id` and
1065/// `Company` doesn't have `user_id`.
1066///
1067/// Instead we need [`LoadFrom`] to return `Vec<(models::Company, models::Employment)>`. We say
1068/// "users have many companies through employments", because `models::Employment` is necessary for
1069/// pairing things up at the end of [`EagerLoadChildrenOfType`][].
1070///
1071/// In this case `JoinModel` would be `models::Employment`.
1072///
1073/// [`HasManyThrough`]: struct.HasManyThrough.html
1074/// [`LoadFrom`]: trait.LoadFrom.html
1075/// [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
1076// `JoinModel` cannot be an associated type because it requires a default.
1077pub trait EagerLoadChildrenOfType<'a, Child, ImplContext, JoinModel = ()>
1078where
1079    Self: GraphqlNodeForModel,
1080    Child: GraphqlNodeForModel<Context = Self::Context, Error = Self::Error>
1081        + EagerLoadAllChildren
1082        + Clone,
1083    JoinModel: 'static + Clone + ?Sized,
1084{
1085    /// The types of arguments the GraphQL field takes. The type used by the code generation can be
1086    /// customized with [`field_arguments = SomeType`][].
1087    ///
1088    /// [`field_arguments = SomeType`]: index.html#fields_arguments
1089    type FieldArguments;
1090
1091    /// Load the children from the data store.
1092    fn load_children(
1093        models: &[Self::Model],
1094        field_args: &Self::FieldArguments,
1095        ctx: &Self::Context,
1096    ) -> Result<LoadChildrenOutput<Child::Model, JoinModel>, Self::Error>;
1097
1098    /// Does this parent and this child belong together?
1099    ///
1100    /// The `join_model` is only used for `HasManyThrough` associations.
1101    fn is_child_of(
1102        parent: &Self,
1103        child: &Child,
1104        join_model: &JoinModel,
1105        field_args: &Self::FieldArguments,
1106        context: &Self::Context,
1107    ) -> bool;
1108
1109    /// Return the particular association type.
1110    ///
1111    /// In most cases the implementation will be something like
1112    ///
1113    /// ```ignore
1114    /// fn association(node: &mut User) -> &mut dyn Association<Country> {
1115    ///     &mut node.country
1116    /// }
1117    /// ```
1118    fn association(node: &mut Self) -> &mut dyn Association<Child>;
1119
1120    /// Combine all the methods above to eager load the children for a list of GraphQL values and
1121    /// models.
1122    fn eager_load_children(
1123        nodes: &mut [Self],
1124        models: &[Self::Model],
1125        ctx: &Self::Context,
1126        trail: &QueryTrail<'a, Child, Walked>,
1127        field_args: &Self::FieldArguments,
1128    ) -> Result<(), Self::Error> {
1129        let child_models = match Self::load_children(models, field_args, ctx)? {
1130            LoadChildrenOutput::ChildModels(child_models) => {
1131                assert!(same_type::<JoinModel, ()>());
1132
1133                child_models
1134                    .into_iter()
1135                    .map(|model| {
1136                        #[allow(unsafe_code)]
1137                        let join_model = unsafe {
1138                            // This branch will only ever be called if `JoinModel` is `()`. That
1139                            // happens for all the `Has*` types except `HasManyThrough`.
1140                            //
1141                            // `HasManyThrough` requires something to join the two types on,
1142                            // therefore `child_ids` will return a variant of `LoadChildrenOutput::Models`
1143                            transmute_copy::<(), JoinModel>(&())
1144                        };
1145
1146                        (model, join_model)
1147                    })
1148                    .collect::<Vec<_>>()
1149            }
1150            LoadChildrenOutput::ChildAndJoinModels(model_and_join_pairs) => model_and_join_pairs,
1151        };
1152
1153        let children = child_models
1154            .iter()
1155            .map(|child_model| (Child::new_from_model(&child_model.0), child_model.1.clone()))
1156            .collect::<Vec<_>>();
1157
1158        let mut children_without_join_models =
1159            children.iter().map(|x| x.0.clone()).collect::<Vec<_>>();
1160
1161        let child_models_without_join_models =
1162            child_models.iter().map(|x| x.0.clone()).collect::<Vec<_>>();
1163
1164        let len_before = child_models_without_join_models.len();
1165
1166        Child::eager_load_all_children_for_each(
1167            &mut children_without_join_models,
1168            &child_models_without_join_models,
1169            ctx,
1170            trail,
1171        )?;
1172
1173        assert_eq!(len_before, child_models_without_join_models.len());
1174
1175        let children = children_without_join_models
1176            .into_iter()
1177            .enumerate()
1178            .map(|(idx, child)| {
1179                let join_model = &children[idx].1;
1180                (child, join_model)
1181            })
1182            .collect::<Vec<_>>();
1183
1184        for node in nodes {
1185            let matching_children = children
1186                .iter()
1187                .filter(|child_model| {
1188                    Self::is_child_of(node, &child_model.0, &child_model.1, field_args, ctx)
1189                })
1190                .cloned()
1191                .collect::<Vec<_>>();
1192
1193            for child in matching_children {
1194                Self::association(node).loaded_child(child.0);
1195            }
1196
1197            Self::association(node).assert_loaded_otherwise_failed();
1198        }
1199
1200        Ok(())
1201    }
1202}
1203
1204/// Are two types the same?
1205fn same_type<A: 'static, B: 'static>() -> bool {
1206    use std::any::TypeId;
1207    TypeId::of::<A>() == TypeId::of::<B>()
1208}
1209
1210/// The result of loading child models.
1211///
1212/// [`HasOne`][], [`OptionHasOne`][], [`HasMany`][] can return the child models directly because
1213/// the model has the foreign key. However for [`HasManyThrough`][] neither the parent or child
1214/// model has any of the foreign keys. Only the join model does. So we have to include those in the
1215/// result.
1216///
1217/// Unless you're customizing [`EagerLoadChildrenOfType`] you shouldn't have to worry about this.
1218///
1219/// [`HasOne`]: struct.HasOne.html
1220/// [`OptionHasOne`]: struct.OptionHasOne.html
1221/// [`HasMany`]: struct.HasMany.html
1222/// [`HasManyThrough`]: struct.HasManyThrough.html
1223/// [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
1224#[derive(Debug)]
1225pub enum LoadChildrenOutput<ChildModel, JoinModel = ()> {
1226    /// Child models were loaded.
1227    ChildModels(Vec<ChildModel>),
1228
1229    /// Child models along with the respective join model was loaded.
1230    ChildAndJoinModels(Vec<(ChildModel, JoinModel)>),
1231}
1232
1233/// The main entry point trait for doing eager loading.
1234///
1235/// You shouldn't need to implement this trait yourself even when customizing eager loading.
1236pub trait EagerLoadAllChildren
1237where
1238    Self: GraphqlNodeForModel,
1239{
1240    /// For each field in your GraphQL type that implements [`EagerLoadChildrenOfType`][] call
1241    /// [`eager_load_children`][] to do eager loading of that field.
1242    ///
1243    /// This is the function you should call for eager loading values for a GraphQL field that returns
1244    /// a list.
1245    ///
1246    /// [`EagerLoadChildrenOfType`]: trait.EagerLoadChildrenOfType.html
1247    /// [`eager_load_children`]: trait.EagerLoadChildrenOfType.html#method.eager_load_children
1248    fn eager_load_all_children_for_each(
1249        nodes: &mut [Self],
1250        models: &[Self::Model],
1251        ctx: &Self::Context,
1252        trail: &QueryTrail<'_, Self, Walked>,
1253    ) -> Result<(), Self::Error>;
1254
1255    /// Perform eager loading for a single GraphQL value.
1256    ///
1257    /// This is the function you should call for eager loading associations of a single value.
1258    fn eager_load_all_children(
1259        node: Self,
1260        models: &[Self::Model],
1261        ctx: &Self::Context,
1262        trail: &QueryTrail<'_, Self, Walked>,
1263    ) -> Result<Self, Self::Error> {
1264        let mut nodes = vec![node];
1265        Self::eager_load_all_children_for_each(&mut nodes, models, ctx, trail)?;
1266
1267        // This is safe because we just made a vec with exactly one element and
1268        // eager_load_all_children_for_each doesn't remove things from the vec
1269        Ok(nodes.remove(0))
1270    }
1271}
1272
1273/// How should associated values actually be loaded?
1274///
1275/// Normally `T` will be your id type but for [`HasMany`][] and [`HasManyThrough`][] it might also
1276/// be other values.
1277///
1278/// If you're using Diesel it is recommend that you use one of [the macros to
1279/// generate](index.html#macros) implementations.
1280///
1281/// `Args` is the type of arguments your GraphQL field takes. This is how we're able to load things
1282/// differently depending the types of arguments. You can learn more
1283/// [here](index.html#eager-loading-fields-that-take-arguments).
1284///
1285/// [`HasMany`]: struct.HasMany.html
1286/// [`HasManyThrough`]: struct.HasManyThrough.html
1287pub trait LoadFrom<T, Args = ()>: Sized {
1288    /// The error type. This must match the error set in `#[eager_loading(error_type = _)]`.
1289    type Error;
1290
1291    /// Your Juniper context type.
1292    ///
1293    /// This will typically contain a database connection or a connection to some external API.
1294    type Context;
1295
1296    /// Perform the load.
1297    fn load(ids: &[T], args: &Args, context: &Self::Context) -> Result<Vec<Self>, Self::Error>;
1298}
1299
1300/// The kinds of errors that can happen when doing eager loading.
1301#[derive(Debug)]
1302#[allow(missing_copy_implementations)]
1303pub enum Error {
1304    /// The association was not loaded.
1305    ///
1306    /// Did you forget to call
1307    /// [`eager_load_all_children_for_each`](trait.EagerLoadAllChildren.html#tymethod.eager_load_all_children_for_each)?
1308    NotLoaded(AssociationType),
1309
1310    /// Loading the association failed. This can only happen when using
1311    /// [`HasOne`](struct.HasOne.html). All the other association types have defaults.
1312    LoadFailed(AssociationType),
1313}
1314
1315impl fmt::Display for Error {
1316    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1317        match self {
1318            Error::NotLoaded(kind) => {
1319                write!(f, "`{:?}` should have been eager loaded, but wasn't", kind)
1320            }
1321            Error::LoadFailed(kind) => write!(f, "Failed to load `{:?}`", kind),
1322        }
1323    }
1324}
1325
1326impl std::error::Error for Error {}
1327
1328/// Remove duplicates from a list.
1329///
1330/// This function is used to remove duplicate ids from
1331/// [`child_ids`](trait.EagerLoadChildrenOfType.html#tymethod.child_ids).
1332pub fn unique<T: Hash + Eq>(items: Vec<T>) -> Vec<T> {
1333    use std::collections::HashSet;
1334
1335    items
1336        .into_iter()
1337        .collect::<HashSet<_>>()
1338        .into_iter()
1339        .collect::<Vec<_>>()
1340}
1341
1342#[cfg(test)]
1343mod test {
1344    #[test]
1345    fn ui() {
1346        let t = trybuild::TestCases::new();
1347        t.pass("tests/compile_pass/*.rs");
1348
1349        // We currently don't have any compile tests that should fail to build
1350        // t.compile_fail("tests/compile_fail/*.rs");
1351    }
1352}