1use core::{fmt, marker::PhantomData, ops::Range};
2
3use std::sync::Arc;
4
5use fallible_iterator::FallibleIterator;
6use postgres_protocol::message::backend::DataRowBody;
7use postgres_types::FromSql;
8use xitca_io::bytes::{Bytes, BytesStr};
9
10use crate::{
11 column::Column,
12 error::{Error, InvalidColumnIndex, WrongType},
13 from_sql::FromSqlExt,
14 types::Type,
15};
16
17use super::{marker, traits::RowIndexAndType};
18
19pub type Row<'r> = GenericRow<&'r [Column], &'r mut Vec<Range<usize>>, marker::Typed>;
21
22pub type RowSimple<'r> = GenericRow<&'r [Column], &'r mut Vec<Range<usize>>, marker::NoTyped>;
24
25pub type RowOwned = GenericRow<Arc<[Column]>, Vec<Range<usize>>, marker::Typed>;
27
28pub type RowSimpleOwned = GenericRow<Arc<[Column]>, Vec<Range<usize>>, marker::NoTyped>;
30
31pub struct GenericRow<C, R, M> {
32 columns: C,
33 body: DataRowBody,
34 ranges: R,
35 _marker: PhantomData<M>,
36}
37
38impl<C, R, M> fmt::Debug for GenericRow<C, R, M>
39where
40 C: AsRef<[Column]>,
41{
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.debug_struct("Row").field("columns", &self.columns.as_ref()).finish()
44 }
45}
46
47impl<C, R, M> GenericRow<C, R, M>
48where
49 C: AsRef<[Column]>,
50 R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
51{
52 pub(crate) fn try_new(columns: C, body: DataRowBody, mut ranges: R) -> Result<Self, Error> {
53 let mut iter = body.ranges();
54
55 let ranges_mut = ranges.as_mut();
56
57 ranges_mut.clear();
58
59 while let Some(range) = iter.next()? {
60 ranges_mut.push(range.unwrap_or(Range { start: 1, end: 0 }));
69 }
70
71 Ok(Self {
72 columns,
73 body,
74 ranges,
75 _marker: PhantomData,
76 })
77 }
78
79 #[inline]
81 pub fn columns(&self) -> &[Column] {
82 self.columns.as_ref()
83 }
84
85 #[inline]
87 pub fn is_empty(&self) -> bool {
88 self.len() == 0
89 }
90
91 #[inline]
93 pub fn len(&self) -> usize {
94 self.columns().len()
95 }
96
97 fn col_buffer(&self, idx: usize) -> (&Range<usize>, &Bytes) {
99 (&self.ranges.as_ref()[idx], self.body.buffer_bytes())
100 }
101
102 fn get_idx_ty<T>(
103 &self,
104 idx: impl RowIndexAndType + fmt::Display,
105 ty_check: impl FnOnce(&Type) -> bool,
106 ) -> Result<(usize, &Type), Error> {
107 let (idx, ty) = idx
108 ._from_columns(self.columns.as_ref())
109 .ok_or_else(|| InvalidColumnIndex(idx.to_string()))?;
110
111 if !ty_check(ty) {
112 return Err(Error::from(WrongType::new::<T>(ty.clone())));
113 }
114
115 Ok((idx, ty))
116 }
117}
118
119impl<M> GenericRow<Arc<[Column]>, Vec<Range<usize>>, M> {
120 pub(crate) fn try_new_owned(columns: &Arc<[Column]>, body: DataRowBody) -> Result<Self, Error> {
121 GenericRow::try_new(columns.clone(), body, Vec::with_capacity(columns.len()))
122 }
123}
124
125impl<C, R> GenericRow<C, R, marker::Typed>
126where
127 C: AsRef<[Column]>,
128 R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
129{
130 pub fn get_zc<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> T
134 where
135 T: FromSqlExt<'s>,
136 {
137 self.try_get_zc(idx)
138 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
139 }
140
141 pub fn try_get_zc<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> Result<T, Error>
143 where
144 T: FromSqlExt<'s>,
145 {
146 let (idx, ty) = self.get_idx_ty::<T>(idx, T::accepts_ext)?;
147 FromSqlExt::from_sql_nullable_ext(ty, self.col_buffer(idx)).map_err(Into::into)
148 }
149
150 pub fn get<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> T
158 where
159 T: FromSql<'s>,
160 {
161 self.try_get(idx)
162 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
163 }
164
165 pub fn try_get<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> Result<T, Error>
167 where
168 T: FromSql<'s>,
169 {
170 let (idx, ty) = self.get_idx_ty::<T>(idx, T::accepts)?;
171 FromSql::from_sql_nullable(ty, self.body.buffer().get(self.ranges.as_ref()[idx].clone())).map_err(Into::into)
172 }
173}
174
175impl<C, R> GenericRow<C, R, marker::NoTyped>
176where
177 C: AsRef<[Column]>,
178 R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
179{
180 pub fn get_zc(&self, idx: impl RowIndexAndType + fmt::Display) -> Option<BytesStr> {
184 self.try_get_zc(idx)
185 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
186 }
187
188 pub fn try_get_zc(&self, idx: impl RowIndexAndType + fmt::Display) -> Result<Option<BytesStr>, Error> {
190 let (idx, ty) = self.get_idx_ty::<BytesStr>(idx, BytesStr::accepts_ext)?;
191 FromSqlExt::from_sql_nullable_ext(ty, self.col_buffer(idx)).map_err(Into::into)
192 }
193
194 pub fn get(&self, idx: impl RowIndexAndType + fmt::Display) -> Option<&str> {
202 self.try_get(idx)
203 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
204 }
205
206 pub fn try_get(&self, idx: impl RowIndexAndType + fmt::Display) -> Result<Option<&str>, Error> {
208 let (idx, ty) = self.get_idx_ty::<&str>(idx, <&str>::accepts)?;
209 FromSql::from_sql_nullable(ty, self.body.buffer().get(self.ranges.as_ref()[idx].clone())).map_err(Into::into)
210 }
211}
212
213fn _try_get(row: Row) {
214 let _ = row.try_get::<u32>(0);
215 let _ = row.try_get_zc::<BytesStr>("test");
216 let _ = row.try_get_zc::<Bytes>(String::from("get_raw").as_str());
217}
218
219fn _try_get_simple(row: RowSimple) {
220 let _ = row.try_get_zc(0);
221 let _ = row.get_zc("test");
222 let _ = row.try_get(String::from("get_raw").as_str());
223}