chuchi_postgres/row/
mod.rs1mod from;
2mod to;
3
4use std::{
5 error::Error as StdError,
6 fmt::Write,
7 pin::Pin,
8 task::{Context, Poll},
9};
10
11use futures_util::Stream;
12use pin_project_lite::pin_project;
13use postgres_types::{FromSql, ToSql};
14pub use tokio_postgres::Column;
15use tokio_postgres::row::RowIndex;
16
17use crate::connection::Error;
18
19pub use from::{FromRow, FromRowOwned};
20pub use to::{ToRow, ToRowStatic};
21
22pub trait NamedColumns {
23 fn select_columns() -> &'static str;
25}
26
27#[derive(Debug)]
28#[repr(transparent)]
29pub struct Row {
30 row: tokio_postgres::Row,
31}
32
33impl Row {
34 pub fn columns(&self) -> &[Column] {
36 self.row.columns()
37 }
38
39 pub fn is_empty(&self) -> bool {
41 self.row.is_empty()
42 }
43
44 pub fn len(&self) -> usize {
46 self.row.len()
47 }
48
49 pub fn deserialize<'a, T>(
51 &'a self,
52 ) -> Result<T, Box<dyn StdError + Sync + Send>>
53 where
54 T: FromRow<'a>,
55 {
56 T::from_row(self)
57 }
58
59 pub fn deserialize_owned<T>(
63 self,
64 ) -> Result<T, Box<dyn StdError + Sync + Send>>
65 where
66 T: FromRowOwned,
67 {
68 T::from_row_owned(self)
69 }
70
71 pub fn get<'a, I, T>(&'a self, idx: I) -> T
79 where
80 I: RowIndex + std::fmt::Display,
81 T: FromSql<'a>,
82 {
83 self.row.get(idx)
84 }
85
86 pub fn try_get<'a, I, T>(
88 &'a self,
89 idx: I,
90 ) -> Result<T, tokio_postgres::Error>
91 where
92 I: RowIndex + std::fmt::Display,
93 T: FromSql<'a>,
94 {
95 self.row.try_get(idx)
96 }
97}
98
99impl From<tokio_postgres::Row> for Row {
100 fn from(row: tokio_postgres::Row) -> Self {
101 Self { row }
102 }
103}
104
105impl FromRowOwned for Row {
106 fn from_row_owned(
107 row: Row,
108 ) -> Result<Self, Box<dyn StdError + Sync + Send>> {
109 Ok(row)
110 }
111}
112
113pin_project! {
114 pub struct RowStream {
115 #[pin]
116 inner: tokio_postgres::RowStream,
117 }
118}
119
120impl Stream for RowStream {
121 type Item = Result<Row, Error>;
122
123 fn poll_next(
124 self: Pin<&mut Self>,
125 cx: &mut Context<'_>,
126 ) -> Poll<Option<Self::Item>> {
127 let this = self.project();
128
129 match this.inner.poll_next(cx) {
130 Poll::Ready(Some(Ok(row))) => Poll::Ready(Some(Ok(row.into()))),
131 Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e.into()))),
132 Poll::Ready(None) => Poll::Ready(None),
133 Poll::Pending => Poll::Pending,
134 }
135 }
136}
137
138impl From<tokio_postgres::RowStream> for RowStream {
139 fn from(inner: tokio_postgres::RowStream) -> Self {
140 Self { inner }
141 }
142}
143
144#[derive(Debug)]
145pub struct RowBuilder<'a> {
146 inner: Vec<(&'a str, &'a (dyn ToSql + Sync))>,
147}
148
149impl<'a> RowBuilder<'a> {
150 pub fn new() -> Self {
151 Self { inner: Vec::new() }
152 }
153
154 pub fn push(
160 &mut self,
161 name: &'a str,
162 value: &'a (dyn ToSql + Sync),
163 ) -> &mut Self {
164 self.inner.push((name, value));
165
166 self
167 }
168}
169
170impl ToRow for RowBuilder<'_> {
171 fn insert_columns(&self, s: &mut String) {
172 for (i, (k, _)) in self.inner.iter().enumerate() {
173 if i != 0 {
174 s.push_str(", ");
175 }
176
177 write!(s, "\"{k}\"").unwrap();
178 }
179 }
180
181 fn insert_values(&self, s: &mut String) {
182 for (i, _) in self.inner.iter().enumerate() {
183 if i != 0 {
184 s.push_str(", ");
185 }
186
187 write!(s, "${}", i + 1).unwrap();
188 }
189 }
190
191 fn update_columns(&self, s: &mut String) {
192 for (i, (k, _)) in self.inner.iter().enumerate() {
193 if i != 0 {
194 s.push_str(", ");
195 }
196
197 write!(s, "\"{k}\" = ${}", i + 1).unwrap();
198 }
199 }
200
201 fn params_len(&self) -> usize {
202 self.inner.len()
203 }
204
205 fn params(&self) -> impl ExactSizeIterator<Item = &(dyn ToSql + Sync)> {
206 self.inner.iter().map(|(_, v)| *v)
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_row_builder() {
216 let mut row = RowBuilder::new();
217 row.push("id", &1i32)
218 .push("name", &"test")
219 .push("email", &"test");
220
221 let mut cols = String::new();
222 row.insert_columns(&mut cols);
223 assert_eq!(cols, r#""id", "name", "email""#);
224
225 let mut values = String::new();
226 row.insert_values(&mut values);
227 assert_eq!(values, r#"$1, $2, $3"#);
228
229 let mut update = String::from("UPDATE \"users\" SET ");
230 row.update_columns(&mut update);
231 assert_eq!(
232 update,
233 r#"UPDATE "users" SET "id" = $1, "name" = $2, "email" = $3"#
234 );
235 }
236}