1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use super::{Alias, AliasSource, AliasedField};

use crate::expression;
use crate::query_source::{Column, Table, TableNotEqual};

/// Serves to map `Self` to `Alias<S>`
///
/// Any column `Self` that belongs to `S::Table` will be transformed into `AliasedField<S, Self>`
///
/// Any column `Self` that does not belong to `S::Table` will be left untouched.
///
/// This also works with tuples and some expressions.
///
// This first part is implemented by the `table!` macro.
// The second part is useful to implement the joins, and may be useful to an end-user for
// ergonomics.
pub trait FieldAliasMapper<S> {
    /// Output type when mapping `C` to `Alias<S>`
    ///
    /// If `C: Column<Table = S::Table>`, `Out = AliasedField<S, C>`  
    /// Otherwise, `Out = C`
    type Out;

    /// Does the mapping
    fn map(self, alias: &Alias<S>) -> Self::Out;
}

#[doc(hidden)]
/// Allows implementing `FieldAliasMapper` in external crates without running into conflicting impl
/// errors due to https://github.com/rust-lang/rust/issues/20400
///
/// We will always have `Self = S::Table` and `CT = C::Table`
pub trait FieldAliasMapperAssociatedTypesDisjointnessTrick<CT, S, C> {
    type Out;
    fn map(column: C, alias: &Alias<S>) -> Self::Out;
}
impl<S, C> FieldAliasMapper<S> for C
where
    S: AliasSource,
    C: Column,
    S::Target: FieldAliasMapperAssociatedTypesDisjointnessTrick<C::Table, S, C>,
{
    type Out = <S::Target as FieldAliasMapperAssociatedTypesDisjointnessTrick<C::Table, S, C>>::Out;

    fn map(self, alias: &Alias<S>) -> Self::Out {
        <S::Target as FieldAliasMapperAssociatedTypesDisjointnessTrick<C::Table, S, C>>::map(
            self, alias,
        )
    }
}

impl<TS, TC, S, C> FieldAliasMapperAssociatedTypesDisjointnessTrick<TC, S, C> for TS
where
    S: AliasSource<Target = TS>,
    C: Column<Table = TC>,
    TC: Table,
    TS: TableNotEqual<TC>,
{
    type Out = C;

    fn map(column: C, _alias: &Alias<S>) -> Self::Out {
        // left untouched because the tables are different
        column
    }
}

macro_rules! field_alias_mapper {
    ($(
        $Tuple:tt {
            $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)+
        }
    )+) => {
        $(
            impl<_S, $($T,)*> FieldAliasMapper<_S> for ($($T,)*)
            where
                _S: AliasSource,
                $($T: FieldAliasMapper<_S>,)*
            {
                type Out = ($(<$T as FieldAliasMapper<_S>>::Out,)*);

                fn map(self, alias: &Alias<_S>) -> Self::Out {
                    (
                        $(self.$idx.map(alias),)*
                    )
                }
            }
        )*
    }
}

diesel_derives::__diesel_for_each_tuple!(field_alias_mapper);

// The following `FieldAliasMapper` impls are useful for the generic join implementations.
// More may be added.
impl<SPrev, SNew, F> FieldAliasMapper<SNew> for AliasedField<SPrev, F>
where
    SNew: AliasSource,
{
    type Out = Self;

    fn map(self, _alias: &Alias<SNew>) -> Self::Out {
        // left untouched because it has already been aliased
        self
    }
}

impl<S, F> FieldAliasMapper<S> for expression::nullable::Nullable<F>
where
    F: FieldAliasMapper<S>,
{
    type Out = expression::nullable::Nullable<<F as FieldAliasMapper<S>>::Out>;

    fn map(self, alias: &Alias<S>) -> Self::Out {
        expression::nullable::Nullable::new(self.0.map(alias))
    }
}

impl<S, F> FieldAliasMapper<S> for expression::grouped::Grouped<F>
where
    F: FieldAliasMapper<S>,
{
    type Out = expression::grouped::Grouped<<F as FieldAliasMapper<S>>::Out>;

    fn map(self, alias: &Alias<S>) -> Self::Out {
        expression::grouped::Grouped(self.0.map(alias))
    }
}