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