1use super::build_filter::BuildFilter;
2use super::common_filter::FilterOption;
3use super::filter_value::FilterValue;
4use super::inner_filter::InnerFilter;
5use super::nullable_filter::IsNull;
6use super::reference_filter::ReferenceFilter;
7use super::Filter;
8use crate::diesel_ext::BoxableFilter;
9use crate::helper::tuple::ConcatTuples;
10use crate::juniper_ext::{FromLookAheadValue, NameBuilder, Nameable};
11use crate::query_builder::selection::fields::WundergraphBelongsTo;
12use crate::query_builder::selection::fields::{FieldListExtractor, NonTableFieldExtractor};
13use crate::query_builder::selection::offset::ApplyOffset;
14use crate::query_builder::selection::LoadingHandler;
15use crate::query_builder::types::{HasMany, HasOne};
16use crate::scalar::WundergraphScalarValue;
17use diesel::associations::HasTable;
18use diesel::backend::Backend;
19use diesel::expression::{NonAggregate, SelectableExpression};
20use diesel::query_builder::QueryFragment;
21use diesel::sql_types::Bool;
22use diesel::Expression;
23use diesel::QuerySource;
24use diesel::Table;
25use indexmap::IndexMap;
26use juniper::meta::Argument;
27use juniper::{FromInputValue, GraphQLType, InputValue, LookAheadValue, Registry, ToInputValue};
28use std::fmt::{self, Debug};
29use std::marker::PhantomData;
30
31pub use wundergraph_derive::BuildFilterHelper;
32
33#[doc(hidden)]
34pub struct FilterWrapper<L, DB, Ctx>
35where
36 FilterConverter<L, DB, Ctx>: CreateFilter,
37{
38 filter: <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter,
39}
40
41impl<L, DB, Ctx> Clone for FilterWrapper<L, DB, Ctx>
42where
43 FilterConverter<L, DB, Ctx>: CreateFilter,
44 <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter: Clone,
45{
46 fn clone(&self) -> Self {
47 Self {
48 filter: self.filter.clone(),
49 }
50 }
51}
52
53impl<L, DB, Ctx> Debug for FilterWrapper<L, DB, Ctx>
54where
55 FilterConverter<L, DB, Ctx>: CreateFilter,
56 <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter: Debug,
57{
58 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59 f.debug_struct("FilterWrapper")
60 .field("inner", &self.filter)
61 .finish()
62 }
63}
64
65#[doc(hidden)]
66#[derive(Debug)]
67pub struct FilterConverter<L, DB, Ctx>(PhantomData<(L, DB, Ctx)>);
68
69#[doc(hidden)]
70#[derive(Debug)]
71pub struct ColumnFilterConverter<N, C, DB, Ctx>(PhantomData<(N, C, DB, Ctx)>);
72
73#[doc(hidden)]
74#[derive(Debug)]
75pub struct NonColumnFilterConveter<N, L, DB, Ctx>(PhantomData<(N, L, DB, Ctx)>);
76
77pub trait CreateFilter {
80 type Filter;
82}
83
84impl<L, DB, Ctx> CreateFilter for FilterConverter<L, DB, Ctx>
85where
86 DB: Backend + ApplyOffset + 'static,
87 DB::QueryBuilder: Default,
88 L::Table: 'static,
89 <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
90 L: LoadingHandler<DB, Ctx>,
91 L::FieldList: FieldListExtractor + NonTableFieldExtractor,
92 ColumnFilterConverter<<L::FieldList as FieldListExtractor>::Out, L::Columns, DB, Ctx>: CreateFilter,
93 NonColumnFilterConveter<<L::FieldList as NonTableFieldExtractor>::Out, L, DB, Ctx>: CreateFilter,
94 <ColumnFilterConverter<<L::FieldList as FieldListExtractor>::Out, L::Columns, DB, Ctx> as CreateFilter>::Filter: ConcatTuples<<
95 NonColumnFilterConveter<<L::FieldList as NonTableFieldExtractor>::Out, L, DB, Ctx> as CreateFilter>::Filter>
96{
97 type Filter = <<ColumnFilterConverter<<L::FieldList as FieldListExtractor>::Out, L::Columns, DB, Ctx> as CreateFilter>::Filter as ConcatTuples<<
98 NonColumnFilterConveter<<L::FieldList as NonTableFieldExtractor>::Out, L, DB, Ctx> as CreateFilter>::Filter>>::Out;
99}
100
101impl<DB, Ctx> CreateFilter for ColumnFilterConverter<(), (), DB, Ctx> {
102 type Filter = ();
103}
104
105impl<DB, L, Ctx> CreateFilter for NonColumnFilterConveter<(), L, DB, Ctx> {
106 type Filter = ();
107}
108
109pub trait AsColumnFilter<C, DB, Ctx> {
112 type Filter;
114}
115
116pub trait AsNonColumnFilter<L, DB, Ctx> {
119 type Filter;
121}
122
123impl<L, O, DB, Ctx, FK> AsNonColumnFilter<L, DB, Ctx> for HasMany<O, FK>
124where
125 L: HasTable,
126 FK: Default + NonAggregate + QueryFragment<DB> + SelectableExpression<O::Table>,
127 O: WundergraphBelongsTo<L::Table, DB, Ctx, FK>,
128 O::Table: 'static,
129 DB: Backend + ApplyOffset + 'static,
130 <O::Table as QuerySource>::FromClause: QueryFragment<DB>,
131 DB::QueryBuilder: Default,
132{
133 type Filter =
134 ReferenceFilter<<L::Table as Table>::PrimaryKey, Filter<O::Filter, O::Table>, FK, ()>;
135}
136
137impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for i16 {
138 type Filter = FilterOption<Self, C>;
139}
140
141impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for i32 {
142 type Filter = FilterOption<Self, C>;
143}
144
145impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for i64 {
146 type Filter = FilterOption<Self, C>;
147}
148
149impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for bool {
150 type Filter = FilterOption<Self, C>;
151}
152
153impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for f32 {
154 type Filter = FilterOption<Self, C>;
155}
156
157impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for f64 {
158 type Filter = FilterOption<Self, C>;
159}
160
161impl<C, DB, Ctx> AsColumnFilter<C, DB, Ctx> for String {
162 type Filter = FilterOption<Self, C>;
163}
164
165impl<C, DB, T, Ctx> AsColumnFilter<C, DB, Ctx> for Vec<T>
166where
167 T: FromLookAheadValue
168 + FromInputValue<WundergraphScalarValue>
169 + ToInputValue<WundergraphScalarValue>
170 + FilterValue<C>
171 + Clone,
172{
173 type Filter = FilterOption<Self, C>;
174}
175
176impl<C, DB, T, Ctx> AsColumnFilter<C, DB, Ctx> for Option<T>
177where
178 T: FilterValue<C>
179 + Clone
180 + FromInputValue<WundergraphScalarValue>
181 + FromLookAheadValue
182 + ToInputValue<WundergraphScalarValue>,
183 T: AsColumnFilter<C, DB, Ctx, Filter = FilterOption<T, C>>,
184{
185 type Filter = FilterOption<Self, C>;
186}
187
188impl<C, K, I, DB, Ctx> AsColumnFilter<C, DB, Ctx> for HasOne<K, I>
189where
190 DB: Backend + ApplyOffset + 'static,
191 I::Table: 'static,
192 I: LoadingHandler<DB, Ctx>,
193 <I::Table as QuerySource>::FromClause: QueryFragment<DB>,
194 DB::QueryBuilder: Default,
195{
196 type Filter =
197 ReferenceFilter<C, Filter<I::Filter, I::Table>, <I::Table as Table>::PrimaryKey, ()>;
198}
199
200#[allow(clippy::use_self)]
202impl<C, K, I, DB, Ctx> AsColumnFilter<C, DB, Ctx> for Option<HasOne<K, I>>
203where
204 DB: Backend + ApplyOffset + 'static,
205 I::Table: 'static,
206 I: LoadingHandler<DB, Ctx>,
207 <I::Table as QuerySource>::FromClause: QueryFragment<DB>,
208 DB::QueryBuilder: Default,
209{
210 type Filter = ReferenceFilter<
211 C,
212 Filter<I::Filter, I::Table>,
213 <I::Table as Table>::PrimaryKey,
214 Option<IsNull<C>>,
215 >;
216}
217
218impl<L, DB, Ctx> Nameable for FilterWrapper<L, DB, Ctx>
219where
220 DB: Backend + ApplyOffset + 'static,
221 L::Table: 'static,
222 L: LoadingHandler<DB, Ctx>,
223 <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
224 DB::QueryBuilder: Default,
225 FilterConverter<L, DB, Ctx>: CreateFilter,
226{
227 fn name() -> String {
228 format!("{}Filter", L::TYPE_NAME)
229 }
230}
231
232pub trait BuildFilterHelper<DB, F, Ctx>
353where
354 DB: Backend,
355{
356 type Ret: Expression<SqlType = Bool> + NonAggregate + QueryFragment<DB>;
358 const FIELD_COUNT: usize;
360
361 fn into_filter(f: F) -> Option<Self::Ret>;
365
366 fn from_inner_look_ahead(objs: &[(&str, LookAheadValue<'_, WundergraphScalarValue>)]) -> F;
368 fn from_inner_input_value(
370 obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>,
371 ) -> Option<F>;
372
373 fn to_inner_input_value(f: &F, _v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>);
375
376 fn register_fields<'r>(
378 _info: &NameBuilder<()>,
379 registry: &mut Registry<'r, WundergraphScalarValue>,
380 ) -> Vec<Argument<'r, WundergraphScalarValue>>;
381}
382
383impl<L, DB, Ctx> BuildFilter<DB> for FilterWrapper<L, DB, Ctx>
384where
385 DB: Backend + ApplyOffset + 'static,
386 L::Table: 'static,
387 L: LoadingHandler<DB, Ctx>,
388 <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
389 DB::QueryBuilder: Default,
390 FilterConverter<L, DB, Ctx>: CreateFilter,
391 L::Table: BuildFilterHelper<DB, <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter, Ctx>,
392{
393 type Ret = <L::Table as BuildFilterHelper<
394 DB,
395 <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter,
396 Ctx,
397 >>::Ret;
398
399 fn into_filter(self) -> Option<Self::Ret> {
400 <L::Table as BuildFilterHelper<DB, _, Ctx>>::into_filter(self.filter)
401 }
402}
403
404#[doc(hidden)]
405#[derive(Debug)]
406pub struct FilterBuildHelper<F, L, DB, Ctx>(pub F, PhantomData<(L, DB, Ctx)>);
407
408impl<F, L, DB, Ctx> Nameable for FilterBuildHelper<F, L, DB, Ctx>
409where
410 DB: Backend + ApplyOffset + 'static,
411 L::Table: 'static,
412 L: LoadingHandler<DB, Ctx>,
413 <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
414 DB::QueryBuilder: Default,
415{
416 fn name() -> String {
417 format!("{}Filter", L::TYPE_NAME)
418 }
419}
420
421impl<L, DB, Ctx> InnerFilter for FilterWrapper<L, DB, Ctx>
422where
423 DB: Backend + ApplyOffset + 'static,
424 L::Table: 'static,
425 L: LoadingHandler<DB, Ctx>,
426 <L::Table as QuerySource>::FromClause: QueryFragment<DB>,
427 DB::QueryBuilder: Default,
428 FilterConverter<L, DB, Ctx>: CreateFilter,
429 L::Table: BuildFilterHelper<DB, <FilterConverter<L, DB, Ctx> as CreateFilter>::Filter, Ctx>,
430{
431 type Context = ();
432
433 const FIELD_COUNT: usize = L::Table::FIELD_COUNT;
434
435 fn from_inner_input_value(
436 obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>,
437 ) -> Option<Self> {
438 Some(Self {
439 filter: L::Table::from_inner_input_value(obj)?,
440 })
441 }
442
443 fn from_inner_look_ahead(objs: &[(&str, LookAheadValue<'_, WundergraphScalarValue>)]) -> Self {
444 Self {
445 filter: L::Table::from_inner_look_ahead(objs),
446 }
447 }
448
449 fn to_inner_input_value(&self, v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>) {
450 L::Table::to_inner_input_value(&self.filter, v)
451 }
452
453 fn register_fields<'r>(
454 _info: &NameBuilder<Self>,
455 registry: &mut Registry<'r, WundergraphScalarValue>,
456 ) -> Vec<Argument<'r, WundergraphScalarValue>> {
457 L::Table::register_fields(&NameBuilder::default(), registry)
458 }
459}
460
461macro_rules! __impl_build_filter_for_tuples {
462 ($(
463 $Tuple:tt {
464 $(($idx:tt) -> $T:ident, $ST: ident, $TT: ident,) +
465 }
466 )+) => {
467 $(
468 impl<$($T,)* Back, Table> BuildFilter<Back> for ($($T,)*)
469 where $($T: BuildFilter<Back, Ret = Box<dyn BoxableFilter<Table, Back, SqlType = Bool>>> + 'static,)*
470 Back: Backend + 'static,
471 Table: 'static
472 {
473 type Ret = Box<dyn BoxableFilter<Table, Back, SqlType = Bool>>;
474
475 fn into_filter(self) -> Option<Self::Ret> {
476 use crate::query_builder::selection::filter::collector::{AndCollector, FilterCollector};
477
478 let mut and = AndCollector::<_, Back>::default();
479 $(
480 and.append_filter(self.$idx);
481 )*
482
483 and.into_filter()
484 }
485 }
486
487 impl<$($T,)* Loading, Back, Ctx> InnerFilter for FilterBuildHelper<($(Option<$T>,)*), Loading, Back, Ctx>
488 where Back: Backend + ApplyOffset + 'static,
489 Loading::Table: 'static,
490 Loading: LoadingHandler<Back, Ctx>,
491 <Loading::Table as QuerySource>::FromClause: QueryFragment<Back>,
492 Back::QueryBuilder: Default,
493 $($T: GraphQLType<WundergraphScalarValue, TypeInfo = NameBuilder<$T>> + ToInputValue<WundergraphScalarValue> + FromInputValue<WundergraphScalarValue> + Nameable + FromLookAheadValue,)*
494 {
495 type Context = ();
496
497 const FIELD_COUNT: usize = $Tuple;
498
499 fn from_inner_input_value(
500 obj: IndexMap<&str, &InputValue<WundergraphScalarValue>>
501 ) -> Option<Self> {
502 let mut values = ($(Option::<$T>::default(),)*);
503 for (name, value) in obj {
504 match name {
505 $(
506 n if n == Loading::FIELD_NAMES[$idx] => {
507 values.$idx = <$T as FromInputValue<WundergraphScalarValue>>::from_input_value(value);
508 }
509 )*
510 _ => {}
511 }
512 }
513 Some(FilterBuildHelper(values, PhantomData))
514 }
515
516 fn from_inner_look_ahead(
517 objs: &[(&str, LookAheadValue<'_, WundergraphScalarValue>)]
518 ) -> Self {
519 let mut values = ($(Option::<$T>::default(),)*);
520 for (name, value) in objs {
521 match name {
522 $(
523 n if *n == Loading::FIELD_NAMES[$idx] => {
524 values.$idx = <$T as FromLookAheadValue>::from_look_ahead(value);
525 }
526 )*
527 _ => {}
528 }
529 }
530 FilterBuildHelper(values, PhantomData)
531 }
532
533 fn to_inner_input_value(
534 &self, v: &mut IndexMap<&str, InputValue<WundergraphScalarValue>>
535 ) {
536 let inner = &self.0;
537 $(
538 let value = <Option<$T> as ToInputValue<WundergraphScalarValue>>::to_input_value(&inner.$idx);
539 v.insert(Loading::FIELD_NAMES[$idx], value);
540 )*
541 }
542
543 fn register_fields<'r>(
544 _info: &NameBuilder<Self>,
545 registry: &mut Registry<'r, WundergraphScalarValue>,
546 ) -> Vec<Argument<'r, WundergraphScalarValue>> {
547 vec![
548 $(
549 registry.arg_with_default::<Option<$T>>(
550 Loading::FIELD_NAMES[$idx],
551 &None,
552 &NameBuilder::default()
553 ),
554 )*
555 ]
556 }
557 }
558
559 impl<$($T,)* $($ST,)* Back, Ctx> CreateFilter for ColumnFilterConverter<($($T,)*), ($($ST,)*), Back, Ctx>
560 where $($T: AsColumnFilter<$ST, Back, Ctx>,)*
561 {
562 type Filter = ($(Option<<$T as AsColumnFilter<$ST, Back, Ctx>>::Filter>,)*);
563 }
564
565 impl<$($T,)* Loading, Back, Ctx> CreateFilter for NonColumnFilterConveter<($($T,)*), Loading, Back, Ctx>
566 where $($T: AsNonColumnFilter<Loading, Back, Ctx>,)*
567 {
568 type Filter = ($(Option<<$T as AsNonColumnFilter<Loading, Back, Ctx>>::Filter>,)*);
569 }
570 )*
571 }
572}
573
574__diesel_for_each_tuple!(__impl_build_filter_for_tuples);