Skip to main content

rustango/sql/executor/
traits.rs

1//! Tri-dialect marker traits for the `_pool` executor surface.
2//!
3//! Each marker trait is feature-gated:
4//! - When the relevant backend feature (`postgres` / `mysql` / `sqlite`) is on,
5//!   the trait has a real bound (e.g. `FromRow<PgRow>`).
6//! - When the feature is off, the trait is empty and blanket-impl'd for every
7//!   `T`, so the bound trivially holds at non-PG / non-MySQL / non-SQLite
8//!   call sites.
9//!
10//! This lets the `_pool` family carry uniform `where T: MaybePgFromRow +
11//! MaybeMyFromRow + MaybeSqliteFromRow` bounds without per-feature signature
12//! divergence. Models derived via `#[derive(Model)]` get the real impls
13//! materialized by the macro's cfg-gated emission.
14//!
15//! Extracted from `executor/mod.rs` as part of #116 step 8.
16
17// ====================================================================
18// `&Pool` FromRow dispatch — Phase B (v0.23.0-batch6)
19// ====================================================================
20//
21// `select_rows_pool` / `FetcherPool::fetch` for `&Pool`. The trait bound on
22// `T` needs to flex: when rustango is built with the `mysql` feature,
23// it must also include `FromRow<MySqlRow>`; without it, the MySql
24// variant doesn't exist and the bound shouldn't either. The
25// [`MaybeMyFromRow`] marker trait below absorbs that conditionality —
26// it's auto-implemented for every type when `mysql` is off, and
27// requires `FromRow<MySqlRow>` when on. Models derived via
28// `#[derive(Model)]` get the right impl automatically: the proc macro
29// emits a call to the cfg-gated `__impl_my_from_row!` macro_rules so
30// the MySQL impl materializes when (and only when) it's needed.
31
32/// Marker trait used as a feature-gated `FromRow<MySqlRow>` bound on
33/// the `_pool` `FromRow`-using executor functions. Auto-implemented
34/// for every `T` when rustango is built without the `mysql` feature
35/// (so PG-only call sites compile unchanged); requires
36/// `FromRow<MySqlRow>` when `mysql` is on.
37#[cfg(feature = "mysql")]
38pub trait MaybeMyFromRow: for<'r> sqlx::FromRow<'r, sqlx::mysql::MySqlRow> {}
39#[cfg(feature = "mysql")]
40impl<T> MaybeMyFromRow for T where T: for<'r> sqlx::FromRow<'r, sqlx::mysql::MySqlRow> {}
41#[cfg(not(feature = "mysql"))]
42pub trait MaybeMyFromRow {}
43#[cfg(not(feature = "mysql"))]
44impl<T> MaybeMyFromRow for T {}
45
46/// MySQL counterpart of [`LoadRelated`]. The proc-macro emits this
47/// alongside the existing `LoadRelated` impl whenever rustango is
48/// built with the `mysql` feature, so `select_related` joins can
49/// stitch parents onto FK fields when decoding from a `MySqlRow`.
50///
51/// FK-less models get a no-op impl; the `MaybeMyLoadRelated` marker
52/// trait wraps this in the same cfg-gated way as
53/// `MaybeMyFromRow` so executor bounds resolve in either feature
54/// config.
55#[cfg(feature = "mysql")]
56pub trait LoadRelatedMy {
57    /// Same contract as [`LoadRelated::__rustango_load_related`] —
58    /// returns `Ok(true)` when `field_name` matched a known FK and
59    /// the parent was decoded successfully, `Ok(false)` for unknown
60    /// field names (graceful skip).
61    ///
62    /// # Errors
63    /// `sqlx::Error` from `try_get` decoding the joined columns.
64    fn __rustango_load_related_my(
65        &mut self,
66        row: &sqlx::mysql::MySqlRow,
67        field_name: &str,
68        alias: &str,
69    ) -> Result<bool, sqlx::Error>;
70}
71
72/// Marker trait used as a feature-gated `LoadRelatedMy` bound on
73/// future `_pool` join-decoding executor functions. Same shape as
74/// [`MaybeMyFromRow`] — auto-implemented for every `T` when
75/// rustango is built without `mysql`; requires `LoadRelatedMy`
76/// when on.
77#[cfg(feature = "mysql")]
78pub trait MaybeMyLoadRelated: LoadRelatedMy {}
79#[cfg(feature = "mysql")]
80impl<T> MaybeMyLoadRelated for T where T: LoadRelatedMy {}
81#[cfg(not(feature = "mysql"))]
82pub trait MaybeMyLoadRelated {}
83#[cfg(not(feature = "mysql"))]
84impl<T> MaybeMyLoadRelated for T {}
85
86// ---- v0.35 — Postgres parallel of the MySQL/SQLite marker traits ----
87//
88// Lets the tri-dialect `_pool` executor functions bound `T` with
89// `MaybePgFromRow + MaybeMyFromRow + MaybeSqliteFromRow` so the
90// signature is satisfiable in any feature configuration. When
91// `postgres` is on the trait requires `FromRow<PgRow>`; when off
92// it's a marker trait blanket-impl'd for every `T`, so the bound
93// trivially holds and the `Pool::Postgres` arm (also gated) never
94// instantiates.
95
96#[cfg(feature = "postgres")]
97pub trait MaybePgFromRow: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow> {}
98#[cfg(feature = "postgres")]
99impl<T> MaybePgFromRow for T where T: for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow> {}
100#[cfg(not(feature = "postgres"))]
101pub trait MaybePgFromRow {}
102#[cfg(not(feature = "postgres"))]
103impl<T> MaybePgFromRow for T {}
104
105// ---- v0.27 Phase 3 — SQLite parallels of the MySQL marker traits ----
106//
107// Same shape as `MaybeMyFromRow` / `LoadRelatedMy` /
108// `MaybeMyLoadRelated`, gated on `sqlite` instead of `mysql`. The
109// `_pool` executor functions add these as additional bounds on `T`
110// so a `Pool::Sqlite` arm can decode `T` from a `SqliteRow`.
111
112#[cfg(feature = "sqlite")]
113pub trait MaybeSqliteFromRow: for<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> {}
114#[cfg(feature = "sqlite")]
115impl<T> MaybeSqliteFromRow for T where T: for<'r> sqlx::FromRow<'r, sqlx::sqlite::SqliteRow> {}
116#[cfg(not(feature = "sqlite"))]
117#[allow(dead_code)]
118pub trait MaybeSqliteFromRow {}
119#[cfg(not(feature = "sqlite"))]
120impl<T> MaybeSqliteFromRow for T {}
121
122/// SQLite counterpart of [`LoadRelated`] / [`LoadRelatedMy`]. The
123/// proc-macro emits this alongside the Postgres + MySQL impls so
124/// `select_related` joins can stitch parents onto FK fields when
125/// decoding from a `SqliteRow`.
126#[cfg(feature = "sqlite")]
127pub trait LoadRelatedSqlite {
128    /// Same contract as [`LoadRelated::__rustango_load_related`].
129    ///
130    /// # Errors
131    /// `sqlx::Error` from `try_get` decoding the joined columns.
132    fn __rustango_load_related_sqlite(
133        &mut self,
134        row: &sqlx::sqlite::SqliteRow,
135        field_name: &str,
136        alias: &str,
137    ) -> Result<bool, sqlx::Error>;
138}
139
140#[cfg(feature = "sqlite")]
141pub trait MaybeSqliteLoadRelated: LoadRelatedSqlite {}
142#[cfg(feature = "sqlite")]
143impl<T> MaybeSqliteLoadRelated for T where T: LoadRelatedSqlite {}
144#[cfg(not(feature = "sqlite"))]
145#[allow(dead_code)]
146pub trait MaybeSqliteLoadRelated {}
147#[cfg(not(feature = "sqlite"))]
148impl<T> MaybeSqliteLoadRelated for T {}