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}