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 ranges_mut.reserve(iter.size_hint().0);
59
60 while let Some(range) = iter.next()? {
61 ranges_mut.push(range.unwrap_or(Range { start: 1, end: 0 }));
70 }
71
72 Ok(Self {
73 columns,
74 body,
75 ranges,
76 _marker: PhantomData,
77 })
78 }
79
80 #[inline]
82 pub fn columns(&self) -> &[Column] {
83 self.columns.as_ref()
84 }
85
86 #[inline]
88 pub fn is_empty(&self) -> bool {
89 self.len() == 0
90 }
91
92 #[inline]
94 pub fn len(&self) -> usize {
95 self.columns().len()
96 }
97
98 fn col_buffer(&self, idx: usize) -> (&Range<usize>, &Bytes) {
100 (&self.ranges.as_ref()[idx], self.body.buffer_bytes())
101 }
102
103 fn get_idx_ty<T>(
104 &self,
105 idx: impl RowIndexAndType + fmt::Display,
106 ty_check: impl FnOnce(&Type) -> bool,
107 ) -> Result<(usize, &Type), Error> {
108 let (idx, ty) = idx
109 ._from_columns(self.columns.as_ref())
110 .ok_or_else(|| InvalidColumnIndex(idx.to_string()))?;
111
112 if !ty_check(ty) {
113 return Err(Error::from(WrongType::new::<T>(ty.clone())));
114 }
115
116 Ok((idx, ty))
117 }
118}
119
120impl<C, R> GenericRow<C, R, marker::Typed>
121where
122 C: AsRef<[Column]>,
123 R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
124{
125 pub fn get_zc<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> T
129 where
130 T: FromSqlExt<'s>,
131 {
132 self.try_get_zc(idx)
133 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
134 }
135
136 pub fn try_get_zc<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> Result<T, Error>
138 where
139 T: FromSqlExt<'s>,
140 {
141 let (idx, ty) = self.get_idx_ty::<T>(idx, T::accepts)?;
142 FromSqlExt::from_sql_nullable_ext(ty, self.col_buffer(idx)).map_err(Into::into)
143 }
144
145 pub fn get<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> T
153 where
154 T: FromSql<'s>,
155 {
156 self.try_get(idx)
157 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
158 }
159
160 pub fn try_get<'s, T>(&'s self, idx: impl RowIndexAndType + fmt::Display) -> Result<T, Error>
162 where
163 T: FromSql<'s>,
164 {
165 let (idx, ty) = self.get_idx_ty::<T>(idx, T::accepts)?;
166 FromSql::from_sql_nullable(ty, self.body.buffer().get(self.ranges.as_ref()[idx].clone())).map_err(Into::into)
167 }
168}
169
170impl<C, R> GenericRow<C, R, marker::NoTyped>
171where
172 C: AsRef<[Column]>,
173 R: AsRef<[Range<usize>]> + AsMut<Vec<Range<usize>>>,
174{
175 pub fn get_zc(&self, idx: impl RowIndexAndType + fmt::Display) -> Option<BytesStr> {
179 self.try_get_zc(idx)
180 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
181 }
182
183 pub fn try_get_zc(&self, idx: impl RowIndexAndType + fmt::Display) -> Result<Option<BytesStr>, Error> {
185 let (idx, ty) = self.get_idx_ty::<BytesStr>(idx, BytesStr::accepts)?;
186 FromSqlExt::from_sql_nullable_ext(ty, self.col_buffer(idx)).map_err(Into::into)
187 }
188
189 pub fn get(&self, idx: impl RowIndexAndType + fmt::Display) -> Option<&str> {
197 self.try_get(idx)
198 .unwrap_or_else(|e| panic!("error retrieving column {idx}: {e}"))
199 }
200
201 pub fn try_get(&self, idx: impl RowIndexAndType + fmt::Display) -> Result<Option<&str>, Error> {
203 let (idx, ty) = self.get_idx_ty::<&str>(idx, <&str as FromSql>::accepts)?;
204 FromSql::from_sql_nullable(ty, self.body.buffer().get(self.ranges.as_ref()[idx].clone())).map_err(Into::into)
205 }
206}
207
208fn _try_get(row: Row) {
209 let _ = row.try_get::<u32>(0);
210 let _ = row.try_get_zc::<BytesStr>("test");
211 let _ = row.try_get_zc::<Bytes>(String::from("get_raw").as_str());
212}
213
214fn _try_get_simple(row: RowSimple) {
215 let _ = row.try_get_zc(0);
216 let _ = row.get_zc("test");
217 let _ = row.try_get(String::from("get_raw").as_str());
218}