queryweave_sql/target.rs
1//! The queries a model's data is written into.
2//!
3//! [`Columns`]' methods check, at compile time, that every field of a model
4//! has a column on the entity, then hand the data and the entity's column
5//! mapping to the query being built: a *target*, implementing one trait per
6//! clause it can take. The derives' generated code is generic over these
7//! traits, so it names no query builder, and a target decides how the clause
8//! is written (sqlx's `QueryBuilder`, with the `sqlx` feature, appends SQL
9//! text and binds the values; sea-query's statements, with the `sea-query`
10//! feature, add expressions; the public helpers of this crate, such as
11//! `like_pattern` and `page_offset`, write it for a target of your own).
12//!
13//! The `write_*` methods do not check the columns themselves: call them
14//! through the entity's `Columns` methods (or the wrappers'
15//! `append_to_query`), which do.
16
17use crate::Columns;
18#[cfg(feature = "merge-patch")]
19use queryweave::FieldValues;
20use queryweave::{Conditions, SortKeys};
21
22/// How conditions are joined: every one must hold (`AND`), or any (`OR`).
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24pub enum Junction {
25 /// Every condition must hold: `… AND …`.
26 And,
27 /// Any condition may hold: `… OR …`.
28 Or,
29}
30
31/// A query that filter conditions can be written into: a `WHERE` clause.
32#[diagnostic::on_unimplemented(
33 message = "`{Self}` is not a query `queryweave-sql` can write conditions into",
34 label = "not a `ConditionsTarget`",
35 note = "enable the `queryweave-sql` feature of your query builder: `sqlx` (on by default) for sqlx's `QueryBuilder`, `sea-query` for sea-query's (and SeaORM's) statements"
36)]
37pub trait ConditionsTarget {
38 /// Start a `WHERE` clause, before its conditions.
39 fn write_where(&mut self);
40
41 /// Write the conditions of the model `M`, joined by `junction`, each
42 /// comparing the column `C` stores its domain field in.
43 ///
44 /// Values are bound with their own SQL type; the `LIKE` operators bind an
45 /// escaped pattern (`… LIKE ? ESCAPE '!'`), so user input matches
46 /// literally.
47 fn write_conditions<C: Columns + ?Sized, M>(
48 &mut self,
49 conditions: Conditions<M>,
50 junction: Junction,
51 );
52}
53
54/// A query that sort keys can be written into: an `ORDER BY` clause.
55#[diagnostic::on_unimplemented(
56 message = "`{Self}` is not a query `queryweave-sql` can write an `ORDER BY` into",
57 label = "not an `OrderByTarget`",
58 note = "enable the `queryweave-sql` feature of your query builder: `sqlx` (on by default) for sqlx's `QueryBuilder`, `sea-query` for sea-query's (and SeaORM's) statements"
59)]
60pub trait OrderByTarget {
61 /// Write an `ORDER BY column direction, …` clause for the sort keys of the
62 /// model `M`, in order, each by the column `C` stores its domain field
63 /// in; nothing when there are none.
64 fn write_order_by<C: Columns + ?Sized, M>(&mut self, sort_keys: SortKeys<M>);
65}
66
67/// A query that a page can be written into: `LIMIT` and `OFFSET`.
68#[diagnostic::on_unimplemented(
69 message = "`{Self}` is not a query `queryweave-sql` can write a page into",
70 label = "not a `PageTarget`",
71 note = "enable the `queryweave-sql` feature of your query builder: `sqlx` (on by default) for sqlx's `QueryBuilder`, `sea-query` for sea-query's (and SeaORM's) statements"
72)]
73pub trait PageTarget {
74 /// Write a `LIMIT page_size OFFSET page * page_size` clause (`page` 0
75 /// based) when both a page and a page size are requested; nothing
76 /// otherwise.
77 fn write_page(&mut self, page: Option<u32>, page_size: Option<u32>);
78}
79
80/// A query that merge-patch writes can be written into: the assignments of an
81/// `UPDATE … SET`.
82#[cfg(feature = "merge-patch")]
83#[diagnostic::on_unimplemented(
84 message = "`{Self}` is not a query `queryweave-sql` can write assignments into",
85 label = "not an `AssignmentsTarget`",
86 note = "enable the `queryweave-sql` feature of your query builder: `sqlx` (on by default) for sqlx's `QueryBuilder`, `sea-query` for sea-query's (and SeaORM's) statements"
87)]
88pub trait AssignmentsTarget {
89 /// What writing the assignments returns, to add more of them (for sqlx,
90 /// the comma-separated builder).
91 type Assignments<'target>
92 where
93 Self: 'target;
94
95 /// Write `column = value` assignments of the model `M`, each to the column
96 /// `C` stores its domain field in.
97 fn write_assignments<C: Columns + ?Sized, M>(
98 &mut self,
99 assignments: FieldValues<M>,
100 ) -> Self::Assignments<'_>;
101}