1use diesel::backend::Backend;
2use diesel::row::{Field, PartialRow, RowIndex, RowSealed};
3use std::{error::Error, num::NonZeroU32};
4use tokio_postgres::{types::Type, Row};
5
6pub struct PgRow {
7 row: Row,
8}
9
10impl PgRow {
11 pub(super) fn new(row: Row) -> Self {
12 Self { row }
13 }
14}
15impl RowSealed for PgRow {}
16
17impl<'a> diesel::row::Row<'a, diesel::pg::Pg> for PgRow {
18 type InnerPartialRow = Self;
19 type Field<'b>
20 = PgField<'b>
21 where
22 Self: 'b,
23 'a: 'b;
24
25 fn field_count(&self) -> usize {
26 self.row.len()
27 }
28
29 fn get<'b, I>(&'b self, idx: I) -> Option<Self::Field<'b>>
30 where
31 'a: 'b,
32 Self: diesel::row::RowIndex<I>,
33 {
34 let idx = self.idx(idx)?;
35 Some(PgField {
36 row: &self.row,
37 idx,
38 })
39 }
40
41 fn partial_row(
42 &self,
43 range: std::ops::Range<usize>,
44 ) -> diesel::row::PartialRow<'_, Self::InnerPartialRow> {
45 PartialRow::new(self, range)
46 }
47}
48
49impl RowIndex<usize> for PgRow {
50 fn idx(&self, idx: usize) -> Option<usize> {
51 if idx < self.row.len() {
52 Some(idx)
53 } else {
54 None
55 }
56 }
57}
58
59impl<'a> RowIndex<&'a str> for PgRow {
60 fn idx(&self, idx: &'a str) -> Option<usize> {
61 self.row.columns().iter().position(|c| c.name() == idx)
62 }
63}
64
65pub struct PgField<'a> {
66 row: &'a Row,
67 idx: usize,
68}
69
70impl<'a> Field<'a, diesel::pg::Pg> for PgField<'a> {
71 fn field_name(&self) -> Option<&str> {
72 Some(self.row.columns()[self.idx].name())
73 }
74
75 fn value(&self) -> Option<<diesel::pg::Pg as Backend>::RawValue<'_>> {
76 let DieselFromSqlWrapper(value) = self.row.get(self.idx);
77 value
78 }
79}
80
81#[repr(transparent)]
82struct TyWrapper(Type);
83
84impl diesel::pg::TypeOidLookup for TyWrapper {
85 fn lookup(&self) -> NonZeroU32 {
86 NonZeroU32::new(self.0.oid()).unwrap()
87 }
88}
89
90struct DieselFromSqlWrapper<'a>(Option<diesel::pg::PgValue<'a>>);
91
92impl<'a> tokio_postgres::types::FromSql<'a> for DieselFromSqlWrapper<'a> {
93 fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, Box<dyn Error + 'static + Send + Sync>> {
94 let ty = unsafe { &*(ty as *const Type as *const TyWrapper) };
95 Ok(DieselFromSqlWrapper(Some(diesel::pg::PgValue::new(
96 raw, ty,
97 ))))
98 }
99
100 fn accepts(ty: &Type) -> bool {
101 ty.oid() != 0
102 }
103
104 fn from_sql_null(_ty: &Type) -> Result<Self, Box<dyn Error + Sync + Send>> {
105 Ok(DieselFromSqlWrapper(None))
106 }
107}