use tokio_postgres::Row;
use crate::error::{PgError, PgResult};
pub trait PgRow {
fn get_value<T>(&self, column: &str) -> PgResult<T>
where
T: for<'a> tokio_postgres::types::FromSql<'a>;
fn get_opt<T>(&self, column: &str) -> PgResult<Option<T>>
where
T: for<'a> tokio_postgres::types::FromSql<'a>;
fn try_get<T>(&self, column: &str) -> Option<T>
where
T: for<'a> tokio_postgres::types::FromSql<'a>;
}
impl PgRow for Row {
fn get_value<T>(&self, column: &str) -> PgResult<T>
where
T: for<'a> tokio_postgres::types::FromSql<'a>,
{
self.try_get(column).map_err(|e| {
PgError::deserialization(format!("failed to get column '{}': {}", column, e))
})
}
fn get_opt<T>(&self, column: &str) -> PgResult<Option<T>>
where
T: for<'a> tokio_postgres::types::FromSql<'a>,
{
match self.try_get(column) {
Ok(value) => Ok(value),
Err(e) => {
if e.to_string().contains("null") {
Ok(None)
} else {
Err(PgError::deserialization(format!(
"failed to get column '{}': {}",
column, e
)))
}
}
}
}
fn try_get<T>(&self, column: &str) -> Option<T>
where
T: for<'a> tokio_postgres::types::FromSql<'a>,
{
Row::try_get(self, column).ok()
}
}
pub trait FromPgRow: Sized {
fn from_row(row: &Row) -> PgResult<Self>;
}
#[macro_export]
macro_rules! impl_from_row {
($type:ident { $($field:ident : $field_type:ty),* $(,)? }) => {
impl $crate::row::FromPgRow for $type {
fn from_row(row: &tokio_postgres::Row) -> $crate::error::PgResult<Self> {
use $crate::row::PgRow;
Ok(Self {
$(
$field: row.get_value(stringify!($field))?,
)*
})
}
}
};
}
#[cfg(test)]
mod tests {
}