|
3 | 3 | //! By default, feature `relations_procmacro` is enabled, exposing macros to |
4 | 4 | //! help build relations. See documentation of the crate `relations_procmacro` |
5 | 5 | //! for more information. |
| 6 | +//! |
| 7 | +//! This module defines types for modeling the relations between |
| 8 | +//! objects, and use them thanks to the `GetCorresponding` custom |
| 9 | +//! derive. |
| 10 | +//! |
| 11 | +//! Let's clarify that with an example. Suppose that `Bike`s have a |
| 12 | +//! `Brand`. `Bike`s also have an `Owner`, and these `Owner`s have a |
| 13 | +//! `Job`. `Bike`s also have a `Kind`. |
| 14 | +//! |
| 15 | +//! ```raw |
| 16 | +//! Brand - Bike - Owner - Job |
| 17 | +//! | |
| 18 | +//! Kind |
| 19 | +//! ``` |
| 20 | +//! |
| 21 | +//! Let's defines these relations and use them a bit: |
| 22 | +//! |
| 23 | +//! ```no_run |
| 24 | +//! # use relations_procmacro::*; |
| 25 | +//! # use relations::*; |
| 26 | +//! # use typed_index_collection::Idx; |
| 27 | +//! # struct Bike; |
| 28 | +//! # struct Brand; |
| 29 | +//! # struct Owner; |
| 30 | +//! # struct Job; |
| 31 | +//! # struct Kind; |
| 32 | +//! # fn get_mbk_brand() -> Idx<Brand> { unimplemented!() } |
| 33 | +//! #[derive(Default, GetCorresponding)] |
| 34 | +//! pub struct World { |
| 35 | +//! brands_to_bikes: OneToMany<Brand, Bike>, |
| 36 | +//! owners_to_bikes: OneToMany<Owner, Bike>, |
| 37 | +//! jobs_to_owners: OneToMany<Job, Owner>, |
| 38 | +//! kinds_to_bikes: OneToMany<Kind, Bike>, |
| 39 | +//! } |
| 40 | +//! let world = World::default(); |
| 41 | +//! let mbk: Idx<Brand> = get_mbk_brand(); |
| 42 | +//! let owners_with_mbk: IdxSet<Owner> = world.get_corresponding_from_idx(mbk); |
| 43 | +//! let jobs_with_mbk: IdxSet<Job> = world.get_corresponding(&owners_with_mbk); |
| 44 | +//! println!( |
| 45 | +//! "{} owners with {} different jobs own a bike of the brand MBK.", |
| 46 | +//! owners_with_mbk.len(), |
| 47 | +//! jobs_with_mbk.len() |
| 48 | +//! ); |
| 49 | +//! ``` |
| 50 | +//! |
| 51 | +//! First, we want to model the relations between the object. One bike |
| 52 | +//! has a brand, and a brand has several bikes (hopefully). Thus, we |
| 53 | +//! use a `OneToMany<Bike, Brand>` to model this relation. |
| 54 | +//! |
| 55 | +//! We repeat this process to model every relation. We obtain without |
| 56 | +//! too much effort the `World` struct. |
| 57 | +//! |
| 58 | +//! The `GetCorresponding` derive looks at each field of the `World` |
| 59 | +//! struct, keeping the fields containing `_to_` with a type with 2 |
| 60 | +//! generics, and interpret that as a relation. For example, |
| 61 | +//! `bikes_to_brands: OneToMany<Bike, Brand>` is a relation between |
| 62 | +//! `Bike` and `Brand`. Using all the relations, it generates a graph, |
| 63 | +//! compute the shortest path between all the types, and generate an |
| 64 | +//! `impl GetCorresponding` for each feasible path. |
| 65 | +//! |
| 66 | +//! These `impl GetCorresponding` are used by |
| 67 | +//! `World::get_corresponding_from_idx` and `World::get_corresponding` |
| 68 | +//! that are helpers to explore the `World`. |
| 69 | +//! |
| 70 | +//! Thus, when we call `world.get_corresponding_from_idx(mbk)` for |
| 71 | +//! `Owner`, we will use the generated code that, basically, gets all |
| 72 | +//! the `Bike`s corresponding to the `Brand` MBK, and then gets all |
| 73 | +//! the `Owner`s corresponding to these `Bike`s. |
| 74 | +//! |
| 75 | +//! Imagine that, in our application, we use a lot the `Owner->Kind` |
| 76 | +//! and `Brand->Kind` search. To do these searches, we pass by |
| 77 | +//! `Bike`, and there is a lot of `Bike`s in our model. Thus, as an |
| 78 | +//! optimization, we want to precompute these relations. |
| 79 | +//! |
| 80 | +//! ```raw |
| 81 | +//! Brand - Bike - Owner - Job |
| 82 | +//! \ | / |
| 83 | +//! `-- Kind --' |
| 84 | +//! ``` |
| 85 | +//! |
| 86 | +//! The shortcuts `Brand - Kind` and `Kind - Owner` allow our |
| 87 | +//! optimization, but we now have a problem for the `Owner->Brand` |
| 88 | +//! search: we can do `Owner->Kind->Brand` and `Owner->Bike->Brand` |
| 89 | +//! with a cost of 2. The first solution is clearly wrong, introduced |
| 90 | +//! by our shortcuts. To fix this problem, we can put a weight of 1.9 |
| 91 | +//! on `Brand - Kind` and `Kind - Owner`. The path |
| 92 | +//! `Owner->Kind->Brand` now cost 3.8 and is discarded. |
| 93 | +//! |
| 94 | +//! Let's implement that: |
| 95 | +//! |
| 96 | +//! ``` |
| 97 | +//! # use relations_procmacro::*; |
| 98 | +//! # use relations::*; |
| 99 | +//! # use typed_index_collection::Idx; |
| 100 | +//! # struct Bike; |
| 101 | +//! # struct Brand; |
| 102 | +//! # struct Owner; |
| 103 | +//! # struct Job; |
| 104 | +//! # struct Kind; |
| 105 | +//! # fn get_mbk_brand() -> Idx<Brand> { unimplemented!() } |
| 106 | +//! #[derive(GetCorresponding)] |
| 107 | +//! pub struct World { |
| 108 | +//! brands_to_bikes: OneToMany<Brand, Bike>, |
| 109 | +//! owners_to_bikes: OneToMany<Owner, Bike>, |
| 110 | +//! jobs_to_owners: OneToMany<Job, Owner>, |
| 111 | +//! kinds_to_bikes: OneToMany<Kind, Bike>, |
| 112 | +//! |
| 113 | +//! // shortcuts |
| 114 | +//! #[get_corresponding(weight = "1.9")] |
| 115 | +//! brands_to_kinds: ManyToMany<Brand, Kind>, |
| 116 | +//! #[get_corresponding(weight = "1.9")] |
| 117 | +//! kinds_to_owners: ManyToMany<Kind, Owner>, |
| 118 | +//! } |
| 119 | +//! # fn create_brands_to_bikes() -> OneToMany<Brand, Bike> { unimplemented!() } |
| 120 | +//! # fn create_owners_to_bikes() -> OneToMany<Owner, Bike> { unimplemented!() } |
| 121 | +//! # fn create_jobs_to_owners() -> OneToMany<Job, Owner> { unimplemented!() } |
| 122 | +//! # fn create_kinds_to_bikes() -> OneToMany<Kind, Bike> { unimplemented!() } |
| 123 | +//! impl World { |
| 124 | +//! fn new() -> World { |
| 125 | +//! let brands_to_bikes = create_brands_to_bikes(); |
| 126 | +//! let owners_to_bikes = create_owners_to_bikes(); |
| 127 | +//! let jobs_to_owners = create_jobs_to_owners(); |
| 128 | +//! let kinds_to_bikes = create_kinds_to_bikes(); |
| 129 | +//! World { |
| 130 | +//! brands_to_kinds: ManyToMany::from_relations_sink( |
| 131 | +//! &brands_to_bikes, |
| 132 | +//! &kinds_to_bikes, |
| 133 | +//! ), |
| 134 | +//! kinds_to_owners: ManyToMany::from_relations_sink( |
| 135 | +//! &kinds_to_bikes, |
| 136 | +//! &owners_to_bikes, |
| 137 | +//! ), |
| 138 | +//! brands_to_bikes, |
| 139 | +//! owners_to_bikes, |
| 140 | +//! jobs_to_owners, |
| 141 | +//! kinds_to_bikes, |
| 142 | +//! } |
| 143 | +//! } |
| 144 | +//! } |
| 145 | +//! ``` |
6 | 146 |
|
7 | 147 | mod error; |
8 | 148 | mod relations; |
|
0 commit comments