juniper_eager_loading/macros.rs
1/// This macro will implement [`LoadFrom`][] for Diesel models using the Postgres backend.
2///
3/// It'll use an [`= ANY`] which is only supported by Postgres.
4///
5/// [`LoadFrom`]: trait.LoadFrom.html
6/// [`= ANY`]: http://docs.diesel.rs/diesel/expression_methods/trait.ExpressionMethods.html#method.eq_any
7///
8/// # Example usage
9///
10/// ```
11/// #[macro_use]
12/// extern crate diesel;
13///
14/// use diesel::pg::PgConnection;
15/// use diesel::prelude::*;
16/// use juniper_eager_loading::impl_load_from_for_diesel_pg;
17/// #
18/// # fn main() {}
19///
20/// table! {
21/// users (id) {
22/// id -> Integer,
23/// }
24/// }
25///
26/// table! {
27/// companies (id) {
28/// id -> Integer,
29/// }
30/// }
31///
32/// table! {
33/// employments (id) {
34/// id -> Integer,
35/// user_id -> Integer,
36/// company_id -> Integer,
37/// }
38/// }
39///
40/// #[derive(Queryable)]
41/// struct User {
42/// id: i32,
43/// }
44///
45/// #[derive(Queryable)]
46/// struct Company {
47/// id: i32,
48/// }
49///
50/// #[derive(Queryable)]
51/// struct Employment {
52/// id: i32,
53/// user_id: i32,
54/// company_id: i32,
55/// }
56///
57/// struct Context {
58/// db: PgConnection,
59/// }
60///
61/// impl Context {
62/// // The macro assumes this method exists
63/// fn db(&self) -> &PgConnection {
64/// &self.db
65/// }
66/// }
67///
68/// impl_load_from_for_diesel_pg! {
69/// (
70/// error = diesel::result::Error,
71/// context = Context,
72/// ) => {
73/// i32 -> (users, User),
74/// i32 -> (companies, Company),
75/// i32 -> (employments, Employment),
76///
77/// User.id -> (employments.user_id, Employment),
78/// Company.id -> (employments.company_id, Employment),
79///
80/// Employment.company_id -> (companies.id, Company),
81/// Employment.user_id -> (users.id, User),
82/// }
83/// }
84/// ```
85///
86/// # Syntax
87///
88/// First you specify your error and connection type with
89///
90/// ```text
91/// (
92/// error = diesel::result::Error,
93/// context = Context,
94/// ) => {
95/// // ...
96/// }
97/// ```
98///
99/// Then you define each model type you want to implement [`LoadFrom`] for and which columns and
100/// tables to use. There are two possible syntaxes for different purposes.
101///
102/// ```text
103/// i32 -> (users, User),
104/// ```
105///
106/// The first syntax implements `LoadFrom<i32> for User`, meaning from a `Vec<i32>` we can load a
107/// `Vec<User>`. It just takes the id type, the table, and the model struct.
108///
109/// ```text
110/// User.id -> (employments.user_id, Employment),
111/// ```
112///
113/// This syntax is required when using [`HasMany`][] and [`HasManyThrough`][]. In this case it
114/// implements `LoadFrom<User> for Employment`, meaning from a `Vec<User>` we can get
115/// `Vec<Employment>`. It does this by loading the users, mapping the list to the user ids,
116/// then finding the employments with those ids.
117///
118/// [`HasMany`]: trait.HasMany.html
119/// [`HasManyThrough`]: trait.HasManyThrough.html
120///
121/// # `Context::db`
122///
123/// It is required that your context type has a method called `db` which returns a reference to a
124/// Diesel connection that can be passed to `.load(_)`.
125///
126/// Example:
127///
128/// ```rust,ignore
129/// struct Context {
130/// db: PgConnection,
131/// }
132///
133/// impl Context {
134/// fn db(&self) -> &PgConnection {
135/// &self.db
136/// }
137/// }
138///
139/// // Whatever the method returns has to work with Diesel's `load` method
140/// users::table
141/// .filter(users::id.eq(any(user_ids)))
142/// .load::<User>(ctx.db())
143/// ```
144///
145/// # What gets generated
146///
147/// The two syntaxes generates code like this:
148///
149/// ```
150/// # #[macro_use]
151/// # extern crate diesel;
152/// # use diesel::pg::PgConnection;
153/// # use diesel::prelude::*;
154/// # use juniper_eager_loading::impl_load_from_for_diesel_pg;
155/// # fn main() {}
156/// # table! {
157/// # users (id) {
158/// # id -> Integer,
159/// # }
160/// # }
161/// # table! {
162/// # companies (id) {
163/// # id -> Integer,
164/// # }
165/// # }
166/// # table! {
167/// # employments (id) {
168/// # id -> Integer,
169/// # user_id -> Integer,
170/// # company_id -> Integer,
171/// # }
172/// # }
173/// # #[derive(Queryable)]
174/// # struct User {
175/// # id: i32,
176/// # }
177/// # #[derive(Queryable)]
178/// # struct Company {
179/// # id: i32,
180/// # }
181/// # #[derive(Queryable)]
182/// # struct Employment {
183/// # id: i32,
184/// # user_id: i32,
185/// # company_id: i32,
186/// # }
187/// # struct Context { db: PgConnection }
188/// # impl Context {
189/// # fn db(&self) -> &PgConnection {
190/// # &self.db
191/// # }
192/// # }
193///
194/// // i32 -> (users, User),
195/// impl juniper_eager_loading::LoadFrom<i32> for User {
196/// type Error = diesel::result::Error;
197/// type Context = Context;
198///
199/// fn load(ids: &[i32], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
200/// use diesel::pg::expression::dsl::any;
201///
202/// users::table
203/// .filter(users::id.eq(any(ids)))
204/// .load::<User>(ctx.db())
205/// .map_err(From::from)
206/// }
207/// }
208///
209/// // User.id -> (employments.user_id, Employment),
210/// impl juniper_eager_loading::LoadFrom<User> for Employment {
211/// type Error = diesel::result::Error;
212/// type Context = Context;
213///
214/// fn load(froms: &[User], field_args: &(), ctx: &Self::Context) -> Result<Vec<Self>, Self::Error> {
215/// use diesel::pg::expression::dsl::any;
216///
217/// let from_ids = froms.iter().map(|other| other.id).collect::<Vec<_>>();
218/// employments::table
219/// .filter(employments::user_id.eq(any(from_ids)))
220/// .load(ctx.db())
221/// .map_err(From::from)
222/// }
223/// }
224/// ```
225#[macro_export]
226macro_rules! impl_load_from_for_diesel_pg {
227 ( $($token:tt)* ) => {
228 $crate::proc_macros::impl_load_from_for_diesel_pg!($($token)*);
229 }
230}
231
232/// This macro will implement [`LoadFrom`][] for Diesel models using the MySQL backend.
233///
234/// For more details see [`impl_load_from_for_diesel_pg`][].
235///
236/// [`impl_load_from_for_diesel_pg`]: macro.impl_load_from_for_diesel_pg.html
237/// [`LoadFrom`]: trait.LoadFrom.html
238///
239/// # Example usage
240///
241/// ```
242/// #[macro_use]
243/// extern crate diesel;
244///
245/// use diesel::mysql::MysqlConnection;
246/// use diesel::prelude::*;
247/// use juniper_eager_loading::impl_load_from_for_diesel_mysql;
248/// #
249/// # fn main() {}
250///
251/// table! {
252/// users (id) {
253/// id -> Integer,
254/// }
255/// }
256///
257/// table! {
258/// companies (id) {
259/// id -> Integer,
260/// }
261/// }
262///
263/// table! {
264/// employments (id) {
265/// id -> Integer,
266/// user_id -> Integer,
267/// company_id -> Integer,
268/// }
269/// }
270///
271/// #[derive(Queryable)]
272/// struct User {
273/// id: i32,
274/// }
275///
276/// #[derive(Queryable)]
277/// struct Company {
278/// id: i32,
279/// }
280///
281/// #[derive(Queryable)]
282/// struct Employment {
283/// id: i32,
284/// user_id: i32,
285/// company_id: i32,
286/// }
287///
288/// struct Context {
289/// db: MysqlConnection,
290/// }
291///
292/// impl Context {
293/// // The macro assumes this method exists
294/// fn db(&self) -> &MysqlConnection {
295/// &self.db
296/// }
297/// }
298///
299/// impl_load_from_for_diesel_mysql! {
300/// (
301/// error = diesel::result::Error,
302/// context = Context,
303/// ) => {
304/// i32 -> (users, User),
305/// i32 -> (companies, Company),
306/// i32 -> (employments, Employment),
307///
308/// User.id -> (employments.user_id, Employment),
309/// Company.id -> (employments.company_id, Employment),
310///
311/// Employment.company_id -> (companies.id, Company),
312/// Employment.user_id -> (users.id, User),
313/// }
314/// }
315/// ```
316#[macro_export]
317macro_rules! impl_load_from_for_diesel_mysql {
318 ( $($token:tt)* ) => {
319 $crate::proc_macros::impl_load_from_for_diesel_mysql!($($token)*);
320 }
321}
322
323/// This macro will implement [`LoadFrom`][] for Diesel models using the SQLite backend.
324///
325/// For more details see [`impl_load_from_for_diesel_pg`][].
326///
327/// [`impl_load_from_for_diesel_pg`]: macro.impl_load_from_for_diesel_pg.html
328/// [`LoadFrom`]: trait.LoadFrom.html
329///
330/// # Example usage
331///
332/// ```
333/// #[macro_use]
334/// extern crate diesel;
335///
336/// use diesel::sqlite::SqliteConnection;
337/// use diesel::prelude::*;
338/// use juniper_eager_loading::impl_load_from_for_diesel_sqlite;
339/// #
340/// # fn main() {}
341///
342/// table! {
343/// users (id) {
344/// id -> Integer,
345/// }
346/// }
347///
348/// table! {
349/// companies (id) {
350/// id -> Integer,
351/// }
352/// }
353///
354/// table! {
355/// employments (id) {
356/// id -> Integer,
357/// user_id -> Integer,
358/// company_id -> Integer,
359/// }
360/// }
361///
362/// #[derive(Queryable)]
363/// struct User {
364/// id: i32,
365/// }
366///
367/// #[derive(Queryable)]
368/// struct Company {
369/// id: i32,
370/// }
371///
372/// #[derive(Queryable)]
373/// struct Employment {
374/// id: i32,
375/// user_id: i32,
376/// company_id: i32,
377/// }
378///
379/// struct Context {
380/// db: SqliteConnection,
381/// }
382///
383/// impl Context {
384/// // The macro assumes this method exists
385/// fn db(&self) -> &SqliteConnection {
386/// &self.db
387/// }
388/// }
389///
390/// impl_load_from_for_diesel_sqlite! {
391/// (
392/// error = diesel::result::Error,
393/// context = Context,
394/// ) => {
395/// i32 -> (users, User),
396/// i32 -> (companies, Company),
397/// i32 -> (employments, Employment),
398///
399/// User.id -> (employments.user_id, Employment),
400/// Company.id -> (employments.company_id, Employment),
401///
402/// Employment.company_id -> (companies.id, Company),
403/// Employment.user_id -> (users.id, User),
404/// }
405/// }
406/// ```
407#[macro_export]
408macro_rules! impl_load_from_for_diesel_sqlite {
409 ( $($token:tt)* ) => {
410 $crate::proc_macros::impl_load_from_for_diesel_sqlite!($($token)*);
411 }
412}