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
use quote::quote;
/// Generate the `AppendConditions` code.
#[must_use]
pub fn code_gen(
parsed: &crate::parse::append_to_query_shared::Parsed<'_>,
mapping_paths: crate::mapping::append_conditions::MappingPaths,
) -> proc_macro2::TokenStream {
let runtime = &mapping_paths.runtime;
let crate::parse::append_to_query_shared::Parsed {
wrapper_ident: name,
inner_ty,
} = parsed;
let columns = mapping_paths.columns;
let diesel = diesel(name, &columns, runtime);
quote! {
impl #runtime::AppendConditions for #name {
fn append_conditions<Q: #runtime::ConditionsTarget>(&self, query: &mut Q, junction: #runtime::Junction) {
// The domain model describes its set filters as data; turning
// them into SQL, against the entity's columns, happens here.
<#columns as #runtime::Columns>::push_conditions(query, self.0.conditions(), junction);
}
}
// Again as an inherent method, so callers use it without importing
// `queryweave_sql::AppendConditions`.
impl #name {
/// Append the set filters to the query as conditions joined by
/// `junction`.
///
/// See the `AppendConditions` trait's `append_conditions`.
pub fn append_conditions<Q: #runtime::ConditionsTarget>(&self, query: &mut Q, junction: #runtime::Junction) {
<Self as #runtime::AppendConditions>::append_conditions(self, query, junction);
}
}
// Every field these filters may name must have a column on the
// entity. `Columns::push_conditions` checks it too, once instantiated; this
// item reports it in `cargo check` as well.
#runtime::assert_columns!(#columns, <#inner_ty as #runtime::PersistedFields>::FIELD_NAMES);
#diesel
}
}
/// The wrapper's Diesel method: the set filters as one Diesel condition, for
/// an entity with `#[diesel(table_name)]` (its `diesel::Entity`), let through
/// the runtime's `if_diesel!` like the entity's own Diesel methods.
fn diesel(name: &syn::Ident, columns: &syn::Path, runtime: &syn::Path) -> proc_macro2::TokenStream {
let diesel = quote! { #runtime::diesel };
quote! {
#runtime::__private::if_diesel! {
impl #name {
/// The set filters as one Diesel condition on the entity's
/// table, joined by `junction`, for a boxed query's `filter`
/// (or `or_filter`); `None` when none are set.
///
/// Builds only for a Diesel entity (one deriving `Columns`
/// with `#[diesel(table_name = ...)]`).
///
/// # Errors
///
/// The `ValueMismatch` of a value that does not fit its
/// column's SQL type.
pub fn diesel_condition<'q, DB>(
&self,
junction: #runtime::Junction,
) -> ::core::result::Result<
::core::option::Option<
#diesel::BoxedCondition<'q, <#columns as #diesel::Entity<'q, DB>>::Table, DB>,
>,
#diesel::ValueMismatch,
>
where
#columns: #diesel::Entity<'q, DB>,
{
<#columns as #diesel::Entity<'q, DB>>::conditions(self.0.conditions(), junction)
}
}
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_code_gen() {
let parsed = crate::parse::append_to_query_shared::Parsed {
wrapper_ident: "e::format_ident!("OrdersFilterOptionsModelWrapper"),
inner_ty: &syn::parse_str("OrdersFilterOptionsModel").unwrap(),
};
let mapping_paths = crate::mapping::append_conditions::MappingPaths {
columns: syn::parse_str("crate::entities::order::Order").unwrap(),
runtime: syn::parse_quote!(::queryweave_sql),
};
let token_stream = super::code_gen(&parsed, mapping_paths);
insta::assert_snapshot!(prettyplease::unparse(
&syn::parse2::<syn::File>(token_stream).unwrap()
));
}
}