I am trying to manually implement sqlx::FrowRow
instead of using derive
, since some custom initialization logic is needed (ex. convert integer i16
to enum).
Default impl is generated like this:
use sqlx::FromRow;
impl FromRow for User {
fn from_row(row: &'r R) -> Result<Self, sqlx::Error> {
todo!()
}
}
But it's unclear, what lifetime and type should I use.
The FromRow
trait is extremely generic since it is used for different backend databases that may support different types. You can see how many generic constraints are needed to implement a simple struct by looking at what #[derive(FromRow)]
generates (via cargo expand
):
use sqlx::FromRow;
#[derive(FromRow)]
struct User {
name: String,
status: i16,
}
// generates
impl<'a, R: ::sqlx::Row> ::sqlx::FromRow<'a, R> for User
where
&'a ::std::primitive::str: ::sqlx::ColumnIndex<R>,
String: ::sqlx::decode::Decode<'a, R::Database>,
String: ::sqlx::types::Type<R::Database>,
i16: ::sqlx::decode::Decode<'a, R::Database>,
i16: ::sqlx::types::Type<R::Database>,
{
fn from_row(row: &'a R) -> ::sqlx::Result<Self> {
let name: String = row.try_get("name")?;
let status: i16 = row.try_get("status")?;
::std::result::Result::Ok(User { name, status })
}
}
It has to constrain that the columns can be indexed by name as well as constrain that String
and i16
are valid and can be decoded from the database.
Doing it yourself, you're probably better off picking which database Row
type you intend to use (AnyRow
, MssqlRow
, MySqlRow
, PgRow
, SqliteRow
) and implement it for that. Here using PgRow
for postgres:
use sqlx::{Row, FromRow, Error};
use sqlx::postgres::PgRow;
struct User {
name: String,
status: i16,
}
impl<'r> FromRow<'r, PgRow> for User {
fn from_row(row: &'r PgRow) -> Result<Self, Error> {
let name = row.try_get("name")?;
let status = row.try_get("status")?;
Ok(User{ name, status })
}
}
I do wonder though if there's a different recommendation for doing custom transformations (via a DTO or some other mechanism).