1use tokio_postgres::Row;
4
5use crate::error::{PgError, PgResult};
6
7pub trait PgRow {
9 fn get_value<T>(&self, column: &str) -> PgResult<T>
11 where
12 T: for<'a> tokio_postgres::types::FromSql<'a>;
13
14 fn get_opt<T>(&self, column: &str) -> PgResult<Option<T>>
16 where
17 T: for<'a> tokio_postgres::types::FromSql<'a>;
18
19 fn try_get<T>(&self, column: &str) -> Option<T>
21 where
22 T: for<'a> tokio_postgres::types::FromSql<'a>;
23}
24
25impl PgRow for Row {
26 fn get_value<T>(&self, column: &str) -> PgResult<T>
27 where
28 T: for<'a> tokio_postgres::types::FromSql<'a>,
29 {
30 self.try_get(column).map_err(|e| {
31 PgError::deserialization(format!("failed to get column '{}': {}", column, e))
32 })
33 }
34
35 fn get_opt<T>(&self, column: &str) -> PgResult<Option<T>>
36 where
37 T: for<'a> tokio_postgres::types::FromSql<'a>,
38 {
39 match self.try_get(column) {
40 Ok(value) => Ok(value),
41 Err(e) => {
42 if e.to_string().contains("null") {
44 Ok(None)
45 } else {
46 Err(PgError::deserialization(format!(
47 "failed to get column '{}': {}",
48 column, e
49 )))
50 }
51 }
52 }
53 }
54
55 fn try_get<T>(&self, column: &str) -> Option<T>
56 where
57 T: for<'a> tokio_postgres::types::FromSql<'a>,
58 {
59 Row::try_get(self, column).ok()
60 }
61}
62
63pub trait FromPgRow: Sized {
65 fn from_row(row: &Row) -> PgResult<Self>;
67}
68
69#[macro_export]
80macro_rules! impl_from_row {
81 ($type:ident { $($field:ident : $field_type:ty),* $(,)? }) => {
82 impl $crate::row::FromPgRow for $type {
83 fn from_row(row: &tokio_postgres::Row) -> $crate::error::PgResult<Self> {
84 use $crate::row::PgRow;
85 Ok(Self {
86 $(
87 $field: row.get_value(stringify!($field))?,
88 )*
89 })
90 }
91 }
92 };
93}
94
95#[cfg(test)]
96mod tests {
97 }