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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! The code generation behind the `queryweave` derives, as an ordinary
//! library.
//!
//! Each derive is a function from the derive's input to the generated code,
//! generic over the [`conventions::Conventions`] it names things by.
//! `queryweave-derive` exports every derive with
//! [`conventions::DefaultConventions`]; a crate wanting other rules writes its
//! own small proc-macro crate calling these functions with its own
//! conventions:
//!
//! ```rust,ignore
//! #[proc_macro_derive(Filters, attributes(filter, sortable, filters, equivalent_field))]
//! pub fn derive_filters(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
//! queryweave_codegen::filters::<MyConventions>(input.into()).into()
//! }
//! ```
//!
//! Every derive is built as a `steps::Derive` pipeline, private to the crate
//! (see `docs/macros.md`).
// The public API: what a proc-macro crate with its own conventions needs,
// besides the derive functions below.
/// Errors spanned to the user's code, for a `Conventions` impl to report a
/// name that does not fit.
// Documented by its own `//!` comment, whose links resolve inside the module.
/// Checks on the names of the structs a derive is placed on or links to.
///
/// The building blocks of `DefaultConventions`, for a `Conventions` impl to
/// reuse.
// The plumbing of the derives' pipelines, private so it can change freely.
/// Struct and field attributes shared by several derives (e.g.
/// `#[equivalent_field(name)]`).
/// Compile-time assertions (e.g. that a mapped field exists) for generated code.
/// Mod containing utils.
// Documented by its own `//!` comment, whose links resolve inside the module.
use Conventions;
use TokenStream;
/// `#[derive(Filters)]`: the filtering, sorting and paging types of a struct,
/// from its `#[filter(lt|eq|gt|sw|c|ew|bool)]` and `#[sortable]` fields.
///
/// The struct is either:
///
/// - a **root** model (no `#[filters(mirrors = ...)]`): it gets the query,
/// filter-options, sort-field and sort-details models handing filters and
/// sorting to the persistence layer as data, and publishes them through
/// `queryweave::Filterable`;
/// - a **mirror** of a model (`#[filters(mirrors = path)]`, e.g. an API DTO):
/// it gets its own sort types and query parameters, converting into the
/// mirrored model's types. A plain mirror names only the core runtime; the
/// flags `#[filters(mirrors = path, serde, utoipa, validator)]` opt into
/// deserializable query parameters (and a `PageDto` alias), API
/// documentation schemas and validation (which needs `serde`), and
/// `schemars` and `garde` into their alternatives (JSON schemas, validation
/// needing no serializer), each needing only its own crate. A field
/// named differently on the mirrored model maps onto it with
/// `#[equivalent_field(name)]`; the query parameters keep the mirror's names.
/// The mirrored model is a root model or another mirror (e.g. a gRPC DTO
/// mirroring an API DTO): a mirror publishes its own types through
/// `queryweave::Filterable` too, every chain ending at a root model, and
/// `queryweave::IntoRootQuery` converts a query straight into the root's.
///
/// Attributes: `filter`, `sortable`, `filters`, `equivalent_field`.
/// `#[derive(MergePatch)]`: the functions merge patching a model with its
/// merge patch model counterpart.
///
/// Every field not marked `#[merge_patch(skip)]` must exist on the merge patch
/// model as an `Option<P>` where `P: Into<FieldType>`; `Some` replaces the
/// field, `None` leaves it untouched.
///
/// Attributes: `merge_patch` (`patch = path` on the struct, `skip` on a
/// field).
/// `#[derive(MergePatchNullableFields)]`: a function listing the nullable
/// fields of a struct used for merge-patching, i.e. its `Option<Option<T>>`
/// fields.
/// `#[derive(RawValue)]`: `queryweave::RawValue` for a value object (a
/// newtype such as `struct Username<T = String>(T);`), whose raw value is the
/// wrapped type's.
/// `#[derive(ValidateNonNullable)]`: a validation function for a merge patch
/// DTO, mirroring a domain merge patch model.
///
/// A field named differently on the domain merge patch model maps onto it with
/// `#[equivalent_field(name)]`; validation errors keep the DTO's field names.
/// The mirrored struct is a merge patch model or another mirror (e.g. a gRPC
/// message mirroring the DTO): every one implements `queryweave::NullableFields`
/// under its own names, every chain ending at a merge patch model.
///
/// Attributes: `validate_non_nullable`, `equivalent_field`.
/// `#[derive(AppendToQuery)]`: the append to query trait on a wrapper of a
/// domain query model.
///
/// `#[append_to_query(columns = Entity, filters = Wrapper)]` names the entity
/// (deriving `Columns`) whose columns store the query's domain fields, and
/// the wrapper building its `WHERE` clause.
///
/// Attributes: `append_to_query` (`columns`, `filters`).
/// `#[derive(AppendConditions)]`: `queryweave_sql::AppendConditions` for a
/// filter-options wrapper, appending its set filters to a query as conditions.
///
/// `#[append_conditions(columns = Entity)]` names the entity (deriving `Columns`) whose columns
/// store the filtered domain fields.
///
/// Attributes: `append_conditions` (`columns`).
/// `#[derive(Columns)]`: `queryweave_sql::Columns` for a `sqlx` entity,
/// recording which column stores each field of the domain model named by
/// `#[columns(model = Model)]`.
///
/// Each entity field is a column (named after the field, or its
/// `#[sqlx(rename = "...")]`) storing the domain field of the same name, or
/// the one named by `#[domain_field(name)]`, which must exist on the domain
/// model. `#[sqlx(skip)]` and `#[sqlx(flatten)]` fields are not columns.
///
/// Attributes: `columns` (`model = path`), `domain_field`.