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
use crateexpand;
use TokenStream;
const CRATE_NAME_STR: &str = "sqlx_utils";
/// Creates a type-safe SQL filter struct with builder methods using SQL-like syntax.
///
/// This macro generates a struct that implements the `SqlFilter` trait, making it
/// suitable for use with repository query methods that accept SQL filters.
///
/// # Syntax
///
/// ```ignore
/// sql_filter! {
/// [attributes]
/// visibility struct StructName {
/// SELECT columns FROM table_name WHERE
/// condition [AND|OR] condition ...
/// }
/// }
/// ```
///
/// ## Columns
///
/// You can select all columns with `*` or specify individual columns:
/// - `SELECT * FROM ...`: Select all columns
/// - `SELECT col1, col2 as alias FROM ...`: Select specific columns with optional aliases
///
/// ## Conditions
///
/// Conditions use SQL-like syntax:
/// ```ignore
/// [?]column_name [as field_name] OPERATOR type
/// ```
///
/// - Optional `?` prefix marks the field as optional in the filter
/// - `column_name` is the database column name
/// - Optional `as field_name` to use a different field name in the generated struct
/// - `OPERATOR` is one of: `=`, `!=`, `>`, `<`, `>=`, `<=`, `LIKE`, `ILIKE`, `IN`, `NOT IN`
/// - `type` is either a Rust type (e.g., `i32`, `String`) or a raw SQL string literal
///
/// Conditions can be combined with logical operators `AND`, `OR`, and `NOT`.
///
/// # Generated Code
///
/// The macro generates:
/// 1. A struct with fields for each condition
/// 2. A constructor method for required fields
/// 3. Builder methods for optional fields
/// 4. Implementation of the `SqlFilter` trait
///
/// # Examples
///
/// ## Basic Filter
///
/// ```rust,ignore
/// # use sqlx_utils_macro::sql_filter;
/// sql_filter! {
/// pub struct UserFilter {
/// SELECT * FROM users WHERE
/// id = i32
/// }
/// }
///
/// // Usage:
/// let filter = UserFilter::new(1);
/// ```
///
/// ## Filter with Optional Fields
///
/// ```rust,ignore
/// # use sqlx_utils_macro::sql_filter;
/// sql_filter! {
/// pub struct UserFilter {
/// SELECT * FROM users WHERE
/// ?name LIKE String AND
/// ?age >= i32
/// }
/// }
///
/// // Usage:
/// let filter = UserFilter::new()
/// .name("John%")
/// .age(18);
/// ```
///
/// ## Filter with Raw SQL
///
/// ```rust,ignore
/// # use sqlx_utils_macro::sql_filter;
/// sql_filter! {
/// pub struct UserFilter {
/// SELECT * FROM users WHERE
/// created_at > "NOW() - INTERVAL '1 day'"
/// }
/// }
/// ```
///
/// ## Complex Filter
///
/// ```rust,ignore
/// # use sqlx_utils_macro::sql_filter;
/// sql_filter! {
/// pub struct OrderFilter {
/// SELECT id, total, name as customer_name FROM orders WHERE
/// id = i32 OR
/// (total > f64 AND ?customer_id = i32)
/// }
/// }
/// ```