sqlx_utils/traits/sql_filter.rs
1//! Sql filtering trait for defining type safe dynamic filters.
2
3use crate::types::Database;
4use sqlx::{Database as DatabaseTrait, QueryBuilder};
5
6/// Trait for creating SQL filter conditions that can be applied to database queries.
7///
8/// The `SqlFilter` trait provides a standardized way to define reusable, type-safe
9/// SQL filter conditions. Filters can be composed and combined using logical operators
10/// to create complex query criteria.
11///
12/// # Type Parameters
13///
14/// * `'args`: The lifetime for query arguments
15/// * `DB`: The database backend type (defaults to the configured default database)
16///
17/// # Examples
18///
19/// Basic implementation using the macro:
20/// ```rust
21/// # use sqlx_utils::sql_filter;
22/// # use sqlx_utils::traits::SqlFilter;
23/// # use sqlx_utils::types::Database;
24/// # use sqlx::QueryBuilder;
25///
26/// sql_filter! {
27/// pub struct UserFilter {
28/// SELECT * FROM users WHERE
29/// id = i32 AND
30/// ?name LIKE String AND
31/// ?email LIKE String
32/// }
33/// }
34///
35/// // Usage
36/// # fn example() {
37/// let filter = UserFilter::new(42)
38/// .name("Alice%")
39/// .email("alice@example.com");
40///
41/// let mut builder = QueryBuilder::<Database>::new("SELECT * FROM users WHERE ");
42/// filter.apply_filter(&mut builder);
43/// # }
44/// ```
45///
46/// Custom implementation:
47/// ```rust
48/// # use sqlx_utils::traits::SqlFilter;
49/// # use sqlx_utils::types::Database;
50/// # use sqlx::QueryBuilder;
51///
52/// struct AgeRangeFilter {
53/// min_age: Option<i32>,
54/// max_age: Option<i32>,
55/// }
56///
57/// impl<'args> SqlFilter<'args, Database> for AgeRangeFilter {
58/// fn apply_filter(self, builder: &mut QueryBuilder<'args, Database>) {
59/// if self.min_age.is_some() || self.max_age.is_some() {
60/// let mut first = true;
61///
62/// if let Some(min) = self.min_age {
63/// if !first { builder.push(" AND "); }
64/// builder.push("age >= ");
65/// builder.push_bind(min);
66/// first = false;
67/// }
68///
69/// if let Some(max) = self.max_age {
70/// if !first { builder.push(" AND "); }
71/// builder.push("age <= ");
72/// builder.push_bind(max);
73/// }
74/// }
75/// }
76///
77/// fn should_apply_filter(&self) -> bool {
78/// self.min_age.is_some() || self.max_age.is_some()
79/// }
80/// }
81/// ```
82///
83/// # Implementation Notes
84///
85/// When implementing this trait:
86///
87/// 1. The [`apply_filter`](SqlFilter::apply_filter) method should add SQL conditions to the builder
88/// 2. The [`should_apply_filter`](SqlFilter::should_apply_filter) method should return `true` if this filter has criteria to apply
89/// 3. Consider using the [`sql_filter!`](crate::sql_filter) macro for common filter patterns
90/// 4. Ensure proper parameterization to prevent SQL injection
91#[diagnostic::on_unimplemented(
92 message = "The filter type `{Self}` must implement `SqlFilter<'args>` to be used in queries",
93 label = "this type does not implement `SqlFilter<'args>`",
94 note = "Type `{Self}` does not implement the `SqlFilter` trait with the required lifetime `'args`.",
95 note = "SqlFilter is implemented by default for the `sqlx_utils::types::Database` database, things might not work if you are using a custom database."
96)]
97pub trait SqlFilter<'args, DB: DatabaseTrait = Database> {
98 /// Applies this filter's conditions to a SQL query builder.
99 ///
100 /// This method should add the necessary SQL conditions represented by this filter
101 /// to the provided query builder. It should handle binding parameters securely.
102 ///
103 /// # Parameters
104 ///
105 /// * `self` - The filter instance, consumed during application
106 /// * `builder` - The query builder to which the filter will be applied
107 fn apply_filter(self, builder: &mut QueryBuilder<'args, DB>);
108
109 /// Determines whether this filter has meaningful conditions to apply.
110 ///
111 /// This method should return `true` if the filter has non-default conditions
112 /// that should be included in a query, or `false` if the filter is empty or
113 /// represents default criteria that don't need to be applied.
114 ///
115 /// # Returns
116 ///
117 /// * `true` - If the filter has conditions to apply
118 /// * `false` - If the filter has no conditions to apply
119 fn should_apply_filter(&self) -> bool;
120}