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 16 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 16 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 16 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
609);
610
611impl RowValues for RowNil {
612    type Values = ();
613    fn into_values(self) {}
614}
615
616impl<K, V, Tail> RowValues for RowCons<K, V, Tail>
617where
618    Tail: RowValues,
619    Tail::Values: Prepend<V>,
620{
621    type Values = <Tail::Values as Prepend<V>>::Output;
622    fn into_values(self) -> Self::Values {
623        self.tail.into_values().prepend(self.value)
624    }
625}
626
627/// `Vec<Row<..>> -> Vec<(..)>`.
628pub trait IntoTuples {
629    type Tuples;
630    fn into_tuples(self) -> Self::Tuples;
631}
632
633impl<L: RowValues> IntoTuples for Vec<Row<L>> {
634    type Tuples = Vec<L::Values>;
635    fn into_tuples(self) -> Vec<L::Values> {
636        self.into_iter().map(Row::into_tuple).collect()
637    }
638}
639
640/// Builds a plain struct out of a row by matching field names, generated by
641/// `#[derive(FromRow)]`. `Idxs` holds the per-field lookup indices, for the
642/// reason `scope::Superset` explains, which is also why this is its own
643/// trait rather than `From`.
644pub trait FromRow<L, Idxs>: Sized {
645    fn from_row(row: Row<L>) -> Self;
646}
647
648/// `Vec<Row<..>> -> Vec<T>` for any `#[derive(FromRow)]` struct.
649pub trait IntoStructs {
650    type Fields;
651    fn into_structs<T, Idxs>(self) -> Vec<T>
652    where
653        T: FromRow<Self::Fields, Idxs>;
654}
655
656impl<L> IntoStructs for Vec<Row<L>> {
657    type Fields = L;
658    fn into_structs<T, Idxs>(self) -> Vec<T>
659    where
660        T: FromRow<L, Idxs>,
661    {
662        self.into_iter().map(Row::into_struct).collect()
663    }
664}
665
666/// Declares an expression's own row key and the `row.<name>()` accessor that
667/// reads it, so a selection using one needs nothing declared at the call
668/// site.
669macro_rules! expr_key {
670    ($key:ident, $accessor:ident, $method:ident, $doc:literal, $($ch:literal),+) => {
671        #[doc = $doc]
672        #[derive(Clone, Copy)]
673        pub struct $key;
674
675        #[doc(hidden)]
676        impl $crate::row::NamedSealed for $key {}
677
678        #[doc(hidden)]
679        impl $crate::row::Named for $key {
680            type Name = $crate::type_name!($($ch),+);
681            const NAME: &'static str = concat!($($ch),+);
682        }
683
684        #[doc(hidden)]
685        impl $crate::row::Spelled for $key {}
686
687        #[doc = $doc]
688        pub trait $accessor<Idx> {
689            type Value;
690            fn $method(&self) -> &Self::Value;
691        }
692
693        impl<L, Idx> $accessor<Idx> for $crate::row::Row<L>
694        where
695            L: $crate::row::Field<$key, Idx>,
696        {
697            type Value = <L as $crate::row::Field<$key, Idx>>::Value;
698            fn $method(&self) -> &Self::Value {
699                self.peek_key::<$key, Idx>()
700            }
701        }
702    };
703}
704pub(crate) use expr_key;