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