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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
//! keelson's derive macros: [`Bind`] for newtype column types, [`FromRow`]
//! for row mapping.
//!
//! Both close the same gap. `docs/type-mappings.md` promises that the Rust
//! type of a generated column can be replaced with your own, and that what a
//! replacement must satisfy is a *trait bound in the generated code* — so a
//! type that cannot bind is a compile error, not a runtime surprise. That
//! bound is `keelson_exec::Bind`:
//!
//! ```text
//! pub trait Bind: ToValue + FromValue + Send + 'static {}
//! impl<T: ToValue + FromValue + Send + 'static> Bind for T {}
//! ```
//!
//! and for every `[[types.override]]` in a keelson-gen configuration the
//! generator emits one line asserting it:
//!
//! ```text
//! const _: () = keelson_exec::assert_bind::<crate::types::UserId>();
//! ```
//!
//! Satisfying that used to mean hand-writing `ToValue` and `FromValue`.
//! `#[derive(Bind)]` writes them for a newtype.
//!
//! # Where this sits
//!
//! The derives, and nothing else. Do not depend on this crate directly:
//! [keelson-core](https://docs.rs/keelson-core) re-exports both behind its `macros` feature,
//! which is the path `use keelson_core::Bind;` takes and the one generated code
//! is written against. The traits they implement live in keelson-core
//! (`ToValue`/`FromValue`) and [keelson-exec](https://docs.rs/keelson-exec) (`FromRow`). The
//! whole map is the [keelson](https://docs.rs/keelson) facade crate.
//!
//! # `#[derive(Bind)]`
//!
//! ```
//! use keelson_core::{Bind, FromValue as _, ToValue as _, Value};
//!
//! #[derive(Debug, Clone, PartialEq, Bind)]
//! pub struct UserId(pub i64);
//!
//! #[derive(Debug, Clone, PartialEq, Bind)]
//! pub struct Email(String);
//!
//! // The line generated code emits for an override now passes.
//! const _: () = keelson_exec::assert_bind::<UserId>();
//!
//! assert_eq!(UserId(7).to_value(), Value::I64(7));
//! assert_eq!(UserId::from_value(Value::I64(7)).unwrap(), UserId(7));
//! ```
//!
//! What it emits, and nothing else: `keelson_core::ToValue` and
//! `keelson_core::FromValue`, each delegating to the single inner field.
//! `Bind` itself is never implemented — it is a blanket alias, and naming the
//! derive after the bound it satisfies is the point. Everything the inner type
//! can do, the newtype now does: `Option<UserId>` binds as NULL-or-value
//! through core's blanket impls, the widening `FromValue` accepts (a driver
//! that hands back `I32` for a `BIGINT`) still applies, and the type is usable
//! anywhere `arg(...)` takes a value.
//!
//! ## What it accepts
//!
//! One field, named or not — `struct UserId(i64);` and
//! `struct UserId { raw: i64 }` are the same thing to this derive. Generics
//! work (`struct Tagged<T>(T)`), with the inner type's bound added to the
//! generated impls.
//!
//! ## What it refuses, and why
//!
//! - **Multi-field structs.** A column is one value. A struct of several is a
//! *row*, which is what [`FromRow`] is for; if the parts genuinely are one
//! column, the encoding is a decision (separator? escaping? what does a
//! malformed value mean?) that belongs in your own `ToValue`/`FromValue`.
//! - **Enums.** Same reason, one level deeper: an enum needs a chosen
//! database representation — text or integer, the spelling of each variant,
//! and what an unrecognised value read back means. Every one of those is a
//! decision this derive would have to invent, and inventing it silently is
//! exactly the "plausible guess" keelson does not make. Write the two impls
//! (about ten lines; the unknown-variant case is
//! `keelson_core::Error::type_mismatch`), or derive `Bind` on a newtype over
//! the representation.
//! - **Unions and unit structs.** Nothing to bind.
//! - **Types with a lifetime parameter.** `FromValue` builds an *owned* value
//! out of a `Value`, so a borrowing newtype could never read back. This one
//! is refused rather than left to the compiler for a specific reason: the
//! bound goes in a `where` clause, and rustc *accepts* an impl whose `where`
//! clause can never hold — it just never applies. The derive would appear to
//! work and then fail at the first call site, which is exactly the distant,
//! inference-swamped failure this whole mechanism exists to replace.
//! - **`#[keelson(...)]` options.** There are none. A newtype has one field
//! and one meaning. The whole `keelson` namespace is refused here rather
//! than only the unrecognised keys, so a `rename` that drifted onto a
//! newtype is caught instead of silently doing nothing. Deriving `Bind` and
//! `FromRow` on the same one-field struct still works — just leave its field
//! attribute-free.
//!
//! Each refusal is a compile error spanned at the offending item, naming the
//! restriction and what to do instead —
//! `keelson-macros/tests/compile_fail/*.stderr` pins the exact text.
//!
//! # `#[derive(FromRow)]`
//!
//! ```
//! use std::sync::Arc;
//!
//! use keelson_core::{FromRow, Value};
//! use keelson_exec::{Column, FromRow as _, Row};
//!
//! #[derive(Debug, PartialEq, FromRow)]
//! struct Account {
//! id: i64,
//! // The column is `email_address`; the field is not.
//! #[keelson(rename = "email_address")]
//! email: String,
//! // A nullable column must be an Option, exactly as in a hand-written impl.
//! nickname: Option<String>,
//! // Read out of the same row, by the nested type's own FromRow.
//! #[keelson(flatten)]
//! audit: Audit,
//! }
//!
//! #[derive(Debug, PartialEq, FromRow)]
//! struct Audit {
//! created_by: i64,
//! }
//!
//! let columns: Arc<[Column]> = vec![
//! Column::new("id"),
//! Column::new("email_address"),
//! Column::new("nickname"),
//! Column::new("created_by"),
//! ]
//! .into();
//! let mut row = Row::new(
//! columns,
//! vec![
//! Value::I64(1),
//! Value::Text("ada@example.com".into()),
//! Value::Null,
//! Value::I64(9),
//! ],
//! );
//!
//! assert_eq!(
//! Account::from_row(&mut row).unwrap(),
//! Account {
//! id: 1,
//! email: "ada@example.com".into(),
//! nickname: None,
//! audit: Audit { created_by: 9 },
//! }
//! );
//! ```
//!
//! The emitted body is the shape `keelson_exec::FromRow` documents and
//! keelson-gen already emits by hand — one `row.take("column")?` per field, by
//! name. By name, not by position, so it survives column reordering and
//! `SELECT *` drift; `take` rather than `get`, so `String`s and blobs move out
//! of the row instead of cloning. Errors keep their column name, because
//! `Row::take` puts it there.
//!
//! ## Field options
//!
//! - `#[keelson(rename = "column")]` — read that column instead of the one
//! named after the field.
//! - `#[keelson(flatten)]` — read the field's own type out of the same row,
//! through its `FromRow` impl. Nested structs, in other words, and it costs
//! one line of generated code because `FromRow::from_row` already takes the
//! whole row.
//!
//! ## What it refuses, and why
//!
//! - **Tuple structs and unit structs.** Mapping is by name, and unnamed
//! fields have none. Tuples up to arity 8 *already* implement `FromRow`
//! positionally, so the alternative is to delete the struct, and the error
//! says so with your own field types substituted in.
//! - **Enums.** Which variant a row is depends on a discriminator column only
//! you can name.
//! - **Structs with a lifetime parameter.** A row is decoded into owned
//! `Value`s and every field is taken out of it by value, so a field
//! borrowing from the row could not outlive the mapping — refused for the
//! same "an unsatisfiable `where` clause compiles" reason as above.
//! - **`rename` together with `flatten`.** One names a single column, the
//! other reads many.
//! - **Two fields reading the same column.** `take` consumes: the value moves
//! out and NULL is left behind, so the second field would silently decode
//! NULL. That is a bug the derive can see, so it is a compile error rather
//! than a mystery at runtime.
//! - **`prefix = "..."`.** Deliberately not implemented, and the error says
//! why rather than pretending the option does not exist. Stripping a prefix
//! means rebuilding the row under different column names before handing it
//! to the nested `FromRow`; the nested impl then reports failures against
//! the *stripped* names ("no column \"id\"" when the result set says
//! "author_id"), and the available-columns list in the error is the stripped
//! set too. An honest prefix needs a prefix-aware view inside
//! `keelson_exec::Row`, which is a change to the execution layer, not to a
//! macro. Until then: `flatten` plus `rename` on the nested fields is
//! explicit, exact, and reports real column names.
//!
//! # Getting at the derives
//!
//! They are re-exported by keelson-core behind its `macros` feature, which is
//! how a user reaches them:
//!
//! ```toml
//! keelson-core = { version = "…", features = ["macros"] }
//! ```
//!
//! `use keelson_core::Bind;` then imports the *derive*; `keelson_exec::Bind`
//! is the *trait*. Importing both is fine — they live in different namespaces
//! — and the trait is rarely named directly, since it is a blanket alias.
//!
//! # What the generated code depends on
//!
//! Nothing is imported into your scope, and nothing you write must be: every
//! path the expansion names is absolute.
//!
//! - `#[derive(Bind)]` names only `::keelson_core` (`ToValue`, `FromValue`,
//! `Value`, `Error`).
//! - `#[derive(FromRow)]` names only `::keelson_exec` (`FromRow`, `Row`,
//! `ExecError`) — plus `::keelson_core` in the one case where a bound must
//! be written out: a *generic* struct, whose emitted `where` clause says
//! `FieldTy: ::keelson_core::FromValue`. A generic `FromRow` struct
//! therefore needs keelson-core in its dependencies; a non-generic one does
//! not.
//!
//! Both crates are dependencies you already have — `FromRow` cannot exist
//! without keelson-exec, and keelson-exec depends on keelson-core.
use TokenStream;
use ;
/// Implement `keelson_core::ToValue` and `keelson_core::FromValue` for a
/// newtype by delegating to its single field — the pair
/// `keelson_exec::Bind` requires, and so the pair a keelson-gen column
/// override must satisfy.
///
/// ```
/// use keelson_core::{Bind, FromValue as _, ToValue as _, Value};
///
/// #[derive(Debug, PartialEq, Bind)]
/// struct UserId(i64);
///
/// const _: () = keelson_exec::assert_bind::<UserId>();
/// assert_eq!(UserId(7).to_value(), Value::I64(7));
/// assert_eq!(UserId::from_value(Value::I64(7)).unwrap(), UserId(7));
/// ```
///
/// Single-field structs only — tuple or named, generic or not. Multi-field
/// structs, enums, unions and unit structs are compile errors naming the
/// restriction; see the crate documentation for the reasoning.
/// Implement `keelson_exec::FromRow` by reading one column per field, by
/// name.
///
/// `#[keelson(rename = "column")]` reads a differently named column;
/// `#[keelson(flatten)]` reads a nested struct out of the same row. Named
/// fields only. See the crate documentation for the full list of what is
/// refused and why.
/// The scanner behind each dialect's `sql!`. Not called directly: the dialect
/// crate's `sql!` forwards to it with its own `raw_query` as the first
/// argument, which is what makes `keelson_sqlite::sql!("…")` know its dialect.
///
/// ```text
/// sql_with!(keelson_sqlite::raw_query, "SELECT … WHERE id = {user_id}")
/// // => keelson_sqlite::raw_query("SELECT … WHERE id = ?").bind(user_id)
/// ```
///
/// See the `sql` module's documentation for the grammar and for what the
/// rewriting is worth.