The compiler tells us that it cannot implicitly convert Hyphenated
to an Option<Uuid>
,
| #[derive(Debug, Serialize, Deserialize, FromRow, PartialEq)]
| ^^^^^^^ the trait `From<Hyphenated>` is not implemented for `std::option::Option<Uuid>`
and we can't implement external traits for external types. The only choices left seem to be
1. Implement the FromRow
trait yourself
Since we know that we'll be using SQLite and the corr_id
is a nullable text column, we can implement FromRow
for sqlx::sqlite::SqliteRow
s. If your struct (row) only has these two fields, this is fine but when extending it with additional fields, you'll need to update your FromRow
implementation as well.
use sqlx::{sqlite::SqliteRow, FromRow, Row};
use uuid::{fmt::Hyphenated, Uuid};
pub struct Transaction {
pub t_id: Uuid,
pub corr_id: Option<Uuid>,
}
impl<'r> FromRow<'r, SqliteRow> for Transaction {
fn from_row(row: &'r SqliteRow) -> Result<Self, sqlx::Error> {
let t_id: Hyphenated = row.try_get("t_id")?;
let corr_id: &str = row.try_get("corr_id")?;
let corr_id = if corr_id.is_empty() {
None
} else {
let uuid = Uuid::try_parse(&corr_id).map_err(|e| sqlx::Error::ColumnDecode {
index: "corr_id".to_owned(),
source: Box::new(e),
})?;
Some(uuid)
};
Ok(Transaction {
t_id: t_id.into(),
corr_id,
})
}
}
This way, you can reuse your "nullable" type in other structs if necessary, and can even implement Deref
, if you want to make extracting the inner UUID easier. It does come with some extra allocations though, since the incoming bytes are converted first to String
, then parsed into Uuid
.
use std::ops::Deref;
use sqlx::FromRow;
#[derive(FromRow)]
pub struct Transaction {
#[sqlx(try_from = "Hyphenated")]
pub t_id: Uuid,
#[sqlx(try_from = "String")]
pub corr_id: NullableUuid,
}
pub struct NullableUuid(Option<Uuid>);
impl TryFrom<String> for NullableUuid {
type Error = uuid::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
let inner = if value.is_empty() {
None
} else {
let uuid = Uuid::try_parse(&value)?;
Some(uuid)
};
Ok(NullableUuid(inner))
}
}
impl Deref for NullableUuid {
type Target = Option<Uuid>;
fn deref(&self) -> &Self::Target {
&self.0
}
}