Skip to main content

qbrs_core/
row.rs

1//! Rows keyed by column rather than by position.
2//!
3//! A tuple selection decodes to `Row<..>`: a type-level list of
4//! `(key, value)` cells. A column's key is its `expr::ColumnKey`, a computed
5//! expression's is the identity it carries (`expr::Count`,
6//! `window::RowNumber`), and `.label(label::..)` supplies one for anything that has
7//! none. `Field` looks a key up the way `scope::Find` looks a table up, with
8//! the same `Here`/`There` index.
9//!
10//! Keying by column is what makes adding a column to a selection a
11//! non-breaking change, and what makes two same-typed columns
12//! (`orders::user_id` and `orders::total` are both `BigInt`) impossible to
13//! transpose. `into_tuple`/`into_tuples` recover the positional view where
14//! destructuring is what's wanted, and `FromRow` fills a plain struct by
15//! matching field *names*, so a DTO names no column and no table.
16//!
17//! Naming a row type takes a type alias long enough to trip
18//! `clippy::type_complexity`, the same lint `qbrs-core` allows crate-wide.
19//! Inference covers every use that stays inside a function.
20//!
21//! **Known limitations**: a key selected twice is ambiguous at the point it
22//! is read, rather than resolving to the first — give one of them a
23//! `label!{}` label. `into_tuple` is implemented up to 32 columns; `Row`
24//! itself has no such limit. A field with no name (a bare `sql!{}`
25//! fragment) can only be reached positionally until `.label(label::..)` gives it one.
26
27use std::marker::PhantomData;
28
29use crate::expr::{Column, ColumnKey, Keyed, Labeled, SqlType};
30use crate::scope::{Here, There};
31
32/// The empty row.
33pub struct RowNil;
34
35/// One field: `V`, filed under key `K`, followed by the rest in `Tail`.
36pub struct RowCons<K, V, Tail> {
37    value: V,
38    tail: Tail,
39    _key: PhantomData<fn() -> K>,
40}
41
42impl<K, V, Tail> RowCons<K, V, Tail> {
43    /// This cell's value. With `tail` and the key's `Named::NAME`, this is
44    /// everything a downstream crate needs to walk a row under whatever
45    /// bounds it wants — `serde::Serialize`, `Display`, anything — which
46    /// `qbrs-core` can't offer itself, having no dependencies.
47    pub fn value(&self) -> &V {
48        &self.value
49    }
50
51    /// The rest of the row.
52    pub fn tail(&self) -> &Tail {
53        &self.tail
54    }
55
56    #[doc(hidden)]
57    pub fn new(value: V, tail: Tail) -> Self {
58        RowCons {
59            value,
60            tail,
61            _key: PhantomData,
62        }
63    }
64
65    /// This cell's value and the rest, by value — what a walk that consumes
66    /// the chain needs (`insert::InsertValues`).
67    #[doc(hidden)]
68    pub fn into_cell(self) -> (V, Tail) {
69        (self.value, self.tail)
70    }
71}
72
73mod field {
74    /// Carries the trait's own parameters and is implemented only for the
75    /// honest pairs, for the reason `scope::proof` explains: a column
76    /// marker is the caller's own type, so a seal on `Self` alone — or a
77    /// private proof *type*, which projection reaches — would let a schema
78    /// crate prove its column is in a row that doesn't hold it.
79    pub trait Sealed<K, Idx> {}
80}
81
82/// Proof that a row holds a field under key `K`, at compile-time-inferred
83/// position `Idx`. `Idx` is never spelled out by callers, exactly as in
84/// `scope::Find`, and is what keeps the two impls below structurally
85/// distinct rather than overlapping.
86///
87/// Borrowing and moving are one trait because they are one search: `pluck`
88/// additionally reports what the row is left holding, so several fields can
89/// be moved out in turn.
90#[diagnostic::on_unimplemented(
91    message = "`{K}` is not in this query's selection",
92    label = "a row can only be read by a key the query selected",
93    note = "add `{K}` to the query's selection list, or `.label(label::..)` the expression you meant — and in a generic helper give each column its own `Idx` parameter, since one shared index matches no row"
94)]
95pub trait Field<K, Idx>: field::Sealed<K, Idx> {
96    type Value;
97    type Rest;
98    fn peek(&self) -> &Self::Value;
99    fn pluck(self) -> (Self::Value, Self::Rest);
100}
101
102impl<K, V, Tail> field::Sealed<K, Here> for RowCons<K, V, Tail> {}
103
104impl<K, V, Tail> Field<K, Here> for RowCons<K, V, Tail> {
105    type Value = V;
106    type Rest = Tail;
107    fn peek(&self) -> &V {
108        &self.value
109    }
110    fn pluck(self) -> (V, Tail) {
111        (self.value, self.tail)
112    }
113}
114
115#[diagnostic::do_not_recommend]
116impl<K, Other, V, Tail, I> field::Sealed<K, There<I>> for RowCons<Other, V, Tail> where
117    Tail: Field<K, I>
118{
119}
120
121impl<K, Other, V, Tail, I> Field<K, There<I>> for RowCons<Other, V, Tail>
122where
123    Tail: Field<K, I>,
124{
125    type Value = <Tail as Field<K, I>>::Value;
126    type Rest = RowCons<Other, V, <Tail as Field<K, I>>::Rest>;
127    fn peek(&self) -> &Self::Value {
128        self.tail.peek()
129    }
130    fn pluck(self) -> (Self::Value, Self::Rest) {
131        let (value, rest) = self.tail.pluck();
132        (value, RowCons::new(self.value, rest))
133    }
134}
135
136/// An identifier spelled one `char` per cell, so two keys declared in
137/// different crates can be compared by the *name* they share rather than by
138/// being the same type. `char` is one of the three types stable const
139/// generics accept.
140#[doc(hidden)]
141pub struct NameChar<const C: char, Rest>(PhantomData<Rest>);
142
143/// End of a `NameChar` chain.
144#[doc(hidden)]
145pub struct NameEnd;
146
147/// Builds a `NameChar` chain from character literals.
148#[doc(hidden)]
149#[macro_export]
150macro_rules! type_name {
151    () => { $crate::row::NameEnd };
152    ($c:literal $(, $rest:literal)*) => {
153        $crate::row::NameChar<$c, $crate::type_name!($($rest),*)>
154    };
155}
156
157/// A key that has a name, so a field can be found by what it is called
158/// rather than by which key type produced it, and so a row can print itself
159/// keyed. Implemented by `#[derive(Table)]` for columns, by `label!` for
160/// labels, and by the built-in expression keys.
161pub trait Named: named::Sealed {
162    type Name;
163    const NAME: &'static str;
164}
165
166pub(crate) mod named {
167    /// Sealed the way `scope::BaseTable` is: a name is written by a macro —
168    /// `#[derive(Table)]`, `with!`, `label!`, or `expr_key!` — so the
169    /// spelling in `Named::NAME` and the one in the SQL cannot disagree.
170    pub trait Sealed {}
171}
172
173#[doc(hidden)]
174pub use named::Sealed as NamedSealed;
175
176/// A key someone wrote down: a column, a `label!`, or one of the built-in
177/// expression keys. `Anon` is deliberately not one, which is what keeps two
178/// unnamed columns from standing in for each other, keeps an unnamed field
179/// out of reach of `.get()`, and keeps a by-name lookup from landing on one.
180pub trait Spelled: Named {}
181
182/// The key of a selected item that carries no name of its own — a bare
183/// `sql!{}` fragment.
184pub struct Anon;
185
186#[doc(hidden)]
187impl NamedSealed for Anon {}
188
189impl Named for Anon {
190    type Name = NameEnd;
191    const NAME: &'static str = "?";
192}
193
194/// What a `#[derive(FromRow)]` field decodes to, declared beside its name
195/// so that a lookup searches for the *pair*. With the type checked
196/// afterwards instead — as an equality on `TakeNamed::Value` — a field whose
197/// type disagrees with the join reports a bare associated-type mismatch at
198/// `into_structs()`, naming neither the field nor the fix.
199pub trait FieldValue {
200    type Value;
201}
202
203mod take_named {
204    /// The same shape as `field::Sealed`, and for the same reason.
205    pub trait Sealed<F, Idx> {}
206}
207
208/// `Field` by name rather than by key identity, which is what lets a struct
209/// that has never heard of `users::email` still receive it.
210#[diagnostic::on_unimplemented(
211    message = "this query's rows have no field matching `{F}`",
212    label = "the selection needs a column of that name, decoding to that type",
213    note = "a computed expression is matched by name only once `.label(label::..)` gives it one, and a LEFT/RIGHT/FULL JOIN makes a column decode as `Option<T>`, so a struct filled from one declares `Option<T>`"
214)]
215pub trait TakeNamed<F, Idx>: take_named::Sealed<F, Idx> {
216    type Value;
217    type Rest;
218    fn take_named(self) -> (Self::Value, Self::Rest);
219}
220
221impl<F, K, V, Tail> take_named::Sealed<F, Here> for RowCons<K, V, Tail>
222where
223    K: Spelled,
224    F: Spelled<Name = <K as Named>::Name> + FieldValue<Value = V>,
225{
226}
227
228impl<F, K, V, Tail> TakeNamed<F, Here> for RowCons<K, V, Tail>
229where
230    K: Spelled,
231    F: Spelled<Name = <K as Named>::Name> + FieldValue<Value = V>,
232{
233    type Value = V;
234    type Rest = Tail;
235    fn take_named(self) -> (V, Tail) {
236        (self.value, self.tail)
237    }
238}
239
240#[diagnostic::do_not_recommend]
241impl<F, K, V, Tail, I> take_named::Sealed<F, There<I>> for RowCons<K, V, Tail> where
242    Tail: TakeNamed<F, I>
243{
244}
245
246impl<F, K, V, Tail, I> TakeNamed<F, There<I>> for RowCons<K, V, Tail>
247where
248    Tail: TakeNamed<F, I>,
249{
250    type Value = <Tail as TakeNamed<F, I>>::Value;
251    type Rest = RowCons<K, V, <Tail as TakeNamed<F, I>>::Rest>;
252    fn take_named(self) -> (Self::Value, Self::Rest) {
253        let (value, rest) = self.tail.take_named();
254        (value, RowCons::new(self.value, rest))
255    }
256}
257
258mod same_name {
259    /// Sealed with the same bounds the one honest impl has: `Self` is a
260    /// column marker local to whoever derived the schema and `Other` is
261    /// free, so without this a schema crate could write
262    /// `impl SameNameAs<a::columns::one> for b::columns::two {}` and splice
263    /// a `UNION` branch or a CTE body in transposed — the failure
264    /// `SameShape` is here to stop.
265    pub trait Sealed<Other> {}
266
267    impl<A, B> Sealed<B> for A
268    where
269        A: super::Spelled,
270        B: super::Spelled<Name = <A as super::Named>::Name>,
271    {
272    }
273}
274
275/// One column can stand in for another: they are called the same thing.
276#[diagnostic::on_unimplemented(
277    message = "`{Self}` can't stand in for `{Other}`",
278    label = "these two selected items must have the same name",
279    note = "matched by name: `.label(label::..)` whichever side is spelled wrong — and an unnamed expression (`Anon`) has no name to match with at all"
280)]
281pub trait SameNameAs<Other>: same_name::Sealed<Other> {}
282
283#[diagnostic::do_not_recommend]
284impl<A, B> SameNameAs<B> for A
285where
286    A: Spelled,
287    B: Spelled<Name = <A as Named>::Name>,
288{
289}
290
291/// Two selections produce the same row: the same column names, in the same
292/// order, decoding to the same types. A one-column selection decodes to a
293/// bare value rather than a `Row`, and two of those match when the value
294/// types do — there is no name to disagree about. Names as well as types, because a
295/// `UNION` branch or a CTE body whose columns merely happen to be
296/// type-compatible would otherwise splice in transposed.
297#[diagnostic::on_unimplemented(
298    message = "these two selections don't produce the same row",
299    label = "must select the same names, in the same order, decoding to the same types"
300)]
301pub trait SameShape<Other> {}
302
303// Walked cell by cell rather than compared as tuples: the positional view
304// stops at 32 fields, and two selections agree or don't regardless of how
305// wide they are. No `do_not_recommend` on the cons impl — it is what keeps
306// the `SameNameAs` obligation the one that gets reported.
307impl SameShape<RowNil> for RowNil {}
308
309impl<K1, K2, V, Tail1, Tail2> SameShape<RowCons<K2, V, Tail2>> for RowCons<K1, V, Tail1>
310where
311    K1: SameNameAs<K2>,
312    Tail1: SameShape<Tail2>,
313{
314}
315
316impl<A, B> SameShape<Row<B>> for Row<A> where A: SameShape<B> {}
317
318/// Maps a value written in a selection list to the type its field is filed
319/// under, so a field is read back with the same value that selected it.
320/// Carries no message of its own: it is reached both from a selection list
321/// (where `SelectionPart` says what belongs in one) and from `.get()`
322/// (where `LookupKey` says what can name a field), and each of those is the
323/// accurate sentence in its position.
324pub trait RowKey {
325    type Key;
326}
327
328#[diagnostic::do_not_recommend]
329impl<C: ColumnKey> RowKey for Column<C> {
330    type Key = C;
331}
332
333#[diagnostic::do_not_recommend]
334impl<K, Req, S: SqlType> RowKey for Keyed<K, Req, S> {
335    type Key = K;
336}
337
338#[diagnostic::do_not_recommend]
339impl<K, Inner> RowKey for Labeled<K, Inner> {
340    type Key = K;
341}
342
343mod column_names {
344    /// Sealed to the two shapes a row has: `CteShape::Row` is bounded by
345    /// `ColumnNames`, so an open impl would let a `WITH` header be spelled
346    /// by something that is not the row `SameShape` checked — and a local
347    /// type in that position is also what makes `SameShape` itself
348    /// forgeable.
349    pub trait Sealed {}
350}
351
352/// The names a declared row spells, in order — read off the row itself so
353/// a `WITH name (..)` header cannot disagree with the shape its body was
354/// checked against. Implemented here only, for `RowNil` and `RowCons`.
355pub trait ColumnNames: column_names::Sealed {
356    /// One `push` per field, so the list is built without an allocation per
357    /// level of the chain.
358    #[doc(hidden)]
359    fn push_names(out: &mut Vec<&'static str>);
360
361    fn names() -> Vec<&'static str> {
362        let mut out = Vec::new();
363        Self::push_names(&mut out);
364        out
365    }
366}
367
368impl column_names::Sealed for RowNil {}
369
370impl ColumnNames for RowNil {
371    fn push_names(_out: &mut Vec<&'static str>) {}
372}
373
374impl<K: Named, V, Tail: ColumnNames> column_names::Sealed for RowCons<K, V, Tail> {}
375
376impl<K: Named, V, Tail: ColumnNames> ColumnNames for RowCons<K, V, Tail> {
377    fn push_names(out: &mut Vec<&'static str>) {
378        out.push(<K as Named>::NAME);
379        Tail::push_names(out);
380    }
381}
382
383/// A value that can name a field at a `.get()`/`.take()` call. Every
384/// `RowKey` can *file* a field; only these can find one again, which is what
385/// keeps an unlabelled expression's `Anon` field out of reach of any other
386/// unlabelled expression. `RowKey where Key: Spelled` would say the same
387/// rule — this exists to carry the message below, which that bound reports
388/// as a bare missing `Spelled` impl on `Anon`.
389#[diagnostic::on_unimplemented(
390    message = "`{Self}` doesn't name a field",
391    label = "an unlabelled expression has no name to look up",
392    note = "give it one with `.label(label::..)`, or read it positionally with `into_tuple()`"
393)]
394pub trait LookupKey: RowKey {}
395
396#[diagnostic::do_not_recommend]
397impl<C: ColumnKey> LookupKey for Column<C> {}
398#[diagnostic::do_not_recommend]
399impl<K: Spelled, Req, S: SqlType> LookupKey for Keyed<K, Req, S> {}
400#[diagnostic::do_not_recommend]
401impl<K: Spelled, Inner> LookupKey for Labeled<K, Inner> {}
402
403/// A decoded row. Its fields are fixed by the query's selection list, and
404/// each is read by the same value that selected it.
405pub struct Row<L>(L);
406
407impl<L> Row<L> {
408    #[doc(hidden)]
409    pub fn new(fields: L) -> Self {
410        Row(fields)
411    }
412
413    /// The row's fields as a `RowCons` chain, for walking it from another
414    /// crate. `get`/`take`/`into_struct` cover reading a known field; this
415    /// is for code that has to visit every field it happens to hold.
416    pub fn fields(&self) -> &L {
417        &self.0
418    }
419
420    /// `row.get(users::email)` — the key is the same value that appeared in
421    /// the selection list, so there is no name to keep in sync and no
422    /// position to get wrong.
423    pub fn get<K: LookupKey, Idx>(&self, _key: K) -> &<L as Field<K::Key, Idx>>::Value
424    where
425        L: Field<K::Key, Idx>,
426    {
427        self.0.peek()
428    }
429
430    /// Moves one field out and hands back the row without it, so several
431    /// fields can be taken in turn.
432    pub fn take<K: LookupKey, Idx>(
433        self,
434        _key: K,
435    ) -> (
436        <L as Field<K::Key, Idx>>::Value,
437        Row<<L as Field<K::Key, Idx>>::Rest>,
438    )
439    where
440        L: Field<K::Key, Idx>,
441    {
442        let (value, rest) = self.0.pluck();
443        (value, Row::new(rest))
444    }
445
446    /// Reads a field by naming its key type rather than passing the value
447    /// that selected it — what the generated accessors use, since a
448    /// built-in expression key is never spelled at a call site.
449    #[doc(hidden)]
450    pub fn peek_key<K, Idx>(&self) -> &<L as Field<K, Idx>>::Value
451    where
452        L: Field<K, Idx>,
453    {
454        self.0.peek()
455    }
456
457    /// `take` by key type rather than by the value that selected it —
458    /// what a `#[from_row(from = ..)]` field uses, since identity is the
459    /// one lookup that stays unambiguous when two columns share a name.
460    #[doc(hidden)]
461    pub fn take_key<K, Idx>(self) -> (<L as Field<K, Idx>>::Value, Row<<L as Field<K, Idx>>::Rest>)
462    where
463        L: Field<K, Idx>,
464    {
465        let (value, rest) = self.0.pluck();
466        (value, Row::new(rest))
467    }
468
469    #[doc(hidden)]
470    pub fn take_named<F, Idx>(
471        self,
472    ) -> (
473        <L as TakeNamed<F, Idx>>::Value,
474        Row<<L as TakeNamed<F, Idx>>::Rest>,
475    )
476    where
477        L: TakeNamed<F, Idx>,
478    {
479        let (value, rest) = self.0.take_named();
480        (value, Row::new(rest))
481    }
482
483    /// Builds a `#[derive(FromRow)]` struct out of this row, matching its
484    /// fields by name. Extra columns in the row are ignored, and the order
485    /// they were selected in doesn't matter.
486    pub fn into_struct<T, Idxs>(self) -> T
487    where
488        T: FromRow<L, Idxs>,
489    {
490        T::from_row(self)
491    }
492
493    /// The positional view: the plain tuple this selection would decode to
494    /// if rows didn't exist.
495    pub fn into_tuple(self) -> L::Values
496    where
497        L: RowValues,
498    {
499        self.0.into_values()
500    }
501}
502
503/// Prints a row keyed, since being keyed is the whole point of the type.
504pub trait DebugFields {
505    fn fmt_fields(&self, f: &mut std::fmt::DebugStruct<'_, '_>);
506}
507
508impl DebugFields for RowNil {
509    fn fmt_fields(&self, _f: &mut std::fmt::DebugStruct<'_, '_>) {}
510}
511
512impl<K: Named, V: std::fmt::Debug, Tail: DebugFields> DebugFields for RowCons<K, V, Tail> {
513    fn fmt_fields(&self, f: &mut std::fmt::DebugStruct<'_, '_>) {
514        f.field(K::NAME, &self.value);
515        self.tail.fmt_fields(f);
516    }
517}
518
519impl<L: DebugFields> std::fmt::Debug for Row<L> {
520    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
521        let mut s = f.debug_struct("Row");
522        self.0.fmt_fields(&mut s);
523        s.finish()
524    }
525}
526
527impl<K, V: Clone, Tail: Clone> Clone for RowCons<K, V, Tail> {
528    fn clone(&self) -> Self {
529        RowCons::new(self.value.clone(), self.tail.clone())
530    }
531}
532
533impl Clone for RowNil {
534    fn clone(&self) -> Self {
535        RowNil
536    }
537}
538
539impl<L: Clone> Clone for Row<L> {
540    fn clone(&self) -> Self {
541        Row(self.0.clone())
542    }
543}
544
545impl<K, V: PartialEq, Tail: PartialEq> PartialEq for RowCons<K, V, Tail> {
546    fn eq(&self, other: &Self) -> bool {
547        self.value == other.value && self.tail == other.tail
548    }
549}
550
551impl PartialEq for RowNil {
552    fn eq(&self, _other: &Self) -> bool {
553        true
554    }
555}
556
557impl<K, V: Eq, Tail: Eq> Eq for RowCons<K, V, Tail> {}
558impl Eq for RowNil {}
559
560impl<L: PartialEq> PartialEq for Row<L> {
561    fn eq(&self, other: &Self) -> bool {
562        self.0 == other.0
563    }
564}
565
566impl<L: Eq> Eq for Row<L> {}
567
568/// A row's fields as a plain tuple, in selection order.
569pub trait RowValues {
570    type Values;
571    fn into_values(self) -> Self::Values;
572}
573
574/// Adds one element to the front of a tuple. The only place `Row`'s
575/// positional view has an arity limit.
576#[diagnostic::on_unimplemented(
577    message = "this row has no positional view",
578    label = "`into_tuple`/`into_tuples` stop at 32 fields, however they were selected",
579    note = "read it by key (`row.get(..)`) or fill a struct with `#[derive(FromRow)]`"
580)]
581pub trait Prepend<H> {
582    type Output;
583    fn prepend(self, head: H) -> Self::Output;
584}
585
586impl<H> Prepend<H> for () {
587    type Output = (H,);
588    fn prepend(self, head: H) -> (H,) {
589        (head,)
590    }
591}
592
593macro_rules! prepend_impls {
594    () => {};
595    ($first:ident $(, $rest:ident)*) => {
596        #[allow(non_snake_case)]
597        impl<H, $first $(, $rest)*> Prepend<H> for ($first, $($rest,)*) {
598            type Output = (H, $first, $($rest,)*);
599            fn prepend(self, head: H) -> Self::Output {
600                let ($first, $($rest,)*) = self;
601                (head, $first, $($rest,)*)
602            }
603        }
604        prepend_impls!($($rest),*);
605    };
606}
607prepend_impls!(
608    T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, T21,
609    T22, T23, T24, T25, T26, T27, T28, T29, T30, T31
610);
611
612impl RowValues for RowNil {
613    type Values = ();
614    fn into_values(self) {}
615}
616
617impl<K, V, Tail> RowValues for RowCons<K, V, Tail>
618where
619    Tail: RowValues,
620    Tail::Values: Prepend<V>,
621{
622    type Values = <Tail::Values as Prepend<V>>::Output;
623    fn into_values(self) -> Self::Values {
624        self.tail.into_values().prepend(self.value)
625    }
626}
627
628/// `Vec<Row<..>> -> Vec<(..)>`.
629pub trait IntoTuples {
630    type Tuples;
631    fn into_tuples(self) -> Self::Tuples;
632}
633
634impl<L: RowValues> IntoTuples for Vec<Row<L>> {
635    type Tuples = Vec<L::Values>;
636    fn into_tuples(self) -> Vec<L::Values> {
637        self.into_iter().map(Row::into_tuple).collect()
638    }
639}
640
641/// Builds a plain struct out of a row by matching field names, generated by
642/// `#[derive(FromRow)]`. `Idxs` holds the per-field lookup indices, for the
643/// reason `scope::Superset` explains, which is also why this is its own
644/// trait rather than `From`.
645pub trait FromRow<L, Idxs>: Sized {
646    fn from_row(row: Row<L>) -> Self;
647}
648
649/// `Vec<Row<..>> -> Vec<T>` for any `#[derive(FromRow)]` struct.
650pub trait IntoStructs {
651    type Fields;
652    fn into_structs<T, Idxs>(self) -> Vec<T>
653    where
654        T: FromRow<Self::Fields, Idxs>;
655}
656
657impl<L> IntoStructs for Vec<Row<L>> {
658    type Fields = L;
659    fn into_structs<T, Idxs>(self) -> Vec<T>
660    where
661        T: FromRow<L, Idxs>,
662    {
663        self.into_iter().map(Row::into_struct).collect()
664    }
665}
666
667/// Declares an expression's own row key and the `row.<name>()` accessor that
668/// reads it, so a selection using one needs nothing declared at the call
669/// site.
670macro_rules! expr_key {
671    ($key:ident, $accessor:ident, $method:ident, $doc:literal, $($ch:literal),+) => {
672        #[doc = $doc]
673        #[derive(Clone, Copy)]
674        pub struct $key;
675
676        #[doc(hidden)]
677        impl $crate::row::NamedSealed for $key {}
678
679        #[doc(hidden)]
680        impl $crate::row::Named for $key {
681            type Name = $crate::type_name!($($ch),+);
682            const NAME: &'static str = concat!($($ch),+);
683        }
684
685        #[doc(hidden)]
686        impl $crate::row::Spelled for $key {}
687
688        #[doc = $doc]
689        pub trait $accessor<Idx> {
690            type Value;
691            fn $method(&self) -> &Self::Value;
692        }
693
694        impl<L, Idx> $accessor<Idx> for $crate::row::Row<L>
695        where
696            L: $crate::row::Field<$key, Idx>,
697        {
698            type Value = <L as $crate::row::Field<$key, Idx>>::Value;
699            fn $method(&self) -> &Self::Value {
700                self.peek_key::<$key, Idx>()
701            }
702        }
703    };
704}
705pub(crate) use expr_key;