fire_postgres/table/column/
data.rs

1#[derive(Debug, Clone, PartialEq)]
2pub enum Text<'a> {
3	Owned(String),
4	Borrowed(&'a str),
5}
6
7impl Text<'_> {
8	pub fn into_string(self) -> String {
9		match self {
10			Self::Owned(s) => s,
11			Self::Borrowed(b) => b.to_string(),
12		}
13	}
14
15	pub fn as_str(&self) -> &str {
16		match self {
17			Self::Owned(s) => s,
18			Self::Borrowed(b) => b,
19		}
20	}
21
22	pub fn len(&self) -> usize {
23		match self {
24			Self::Owned(s) => s.len(),
25			Self::Borrowed(s) => s.len(),
26		}
27	}
28}
29
30impl<'a> From<&'a str> for Text<'a> {
31	fn from(s: &'a str) -> Self {
32		Self::Borrowed(s)
33	}
34}
35
36impl From<String> for Text<'_> {
37	fn from(s: String) -> Self {
38		Self::Owned(s)
39	}
40}
41
42//impl From<
43
44pub enum SliceState<'a> {
45	Owned(&'a [String]),
46	Borrowed(&'a [&'a str]),
47}
48
49#[derive(Debug, Clone, PartialEq)]
50pub enum TextArray<'a> {
51	SliceOwned(&'a [String]),
52	SliceStr(&'a [&'a str]),
53	VecOwned(Vec<String>),
54	VecStr(Vec<&'a str>),
55}
56
57impl<'a> TextArray<'a> {
58	pub fn len(&self) -> usize {
59		match self {
60			Self::SliceOwned(v) => v.len(),
61			Self::SliceStr(v) => v.len(),
62			Self::VecOwned(v) => v.len(),
63			Self::VecStr(v) => v.len(),
64		}
65	}
66
67	pub fn into_vec_owned(self) -> Vec<String> {
68		match self {
69			Self::SliceOwned(s) => s.iter().map(|v| v.to_string()).collect(),
70			Self::SliceStr(s) => s.iter().map(|v| v.to_string()).collect(),
71			Self::VecOwned(v) => v,
72			Self::VecStr(v) => v.iter().map(|v| v.to_string()).collect(),
73		}
74	}
75
76	/*pub fn as_slice_str(&self) -> Vec<&str> {
77		match self {
78			Self::SliceOwned(s) => s.iter(),
79			Self::SliceStr(s) => s.iter().collect(),
80			Self::VecOwned(s) => s.iter().collect(),
81			Self::VecStr(s) => s
82		}
83	}*/
84
85	/*pub fn to_vec_str(self) -> Vec<&'a str> {
86		match self {
87			Self::SliceOwned(s) => s.iter().map(|v| v.as_str()).collect(),
88			Self::SliceStr(s) => s.iter().map(|v| *v).collect(),
89			Self::VecOwned(s) => s.into_iter().map(|v| v.as_str()).collect(),
90			Self::VecStr(v) => v
91		}
92	}*/
93
94	pub fn unwrap_vec_str(self) -> Vec<&'a str> {
95		match self {
96			Self::VecStr(v) => v,
97			_ => panic!("could not unwrap vec str from textarray"),
98		}
99	}
100
101	/*pub fn try_slice_owned(&self) -> Option<&[String]> {
102		Some(match self {
103			Self::SliceOwned(v) => v,
104			Self::VecOwned(v) => v.as_slice(),
105			_ => return None
106		})
107	}*/
108
109	pub fn to_slice_state(&self) -> SliceState<'_> {
110		match self {
111			Self::SliceOwned(v) => SliceState::Owned(v),
112			Self::SliceStr(v) => SliceState::Borrowed(v),
113			Self::VecOwned(v) => SliceState::Owned(v.as_slice()),
114			Self::VecStr(v) => SliceState::Borrowed(v.as_slice()),
115		}
116	}
117}
118
119impl<'a> From<&'a [String]> for TextArray<'a> {
120	fn from(ar: &'a [String]) -> Self {
121		Self::SliceOwned(ar)
122	}
123}
124
125impl<'a> From<&'a [&'a str]> for TextArray<'a> {
126	fn from(ar: &'a [&'a str]) -> Self {
127		Self::SliceStr(ar)
128	}
129}
130
131impl From<Vec<String>> for TextArray<'_> {
132	fn from(ar: Vec<String>) -> Self {
133		Self::VecOwned(ar)
134	}
135}
136
137impl<'a> From<Vec<&'a str>> for TextArray<'a> {
138	fn from(ar: Vec<&'a str>) -> Self {
139		Self::VecStr(ar)
140	}
141}
142
143/*impl TextArray {
144	pub fn next() -> Result<Option<Text>, FromDataError> {
145
146	}
147}*/
148
149// Owned and referenced??
150#[derive(Debug, Clone, PartialEq)]
151pub enum ColumnData<'a> {
152	Boolean(bool),
153	Text(Text<'a>),
154	Date(i32),
155	Timestamp(i64),
156	F64(f64),
157	F32(f32),
158	I64(i64),
159	I32(i32),
160	I16(i16),
161	Option(Option<Box<ColumnData<'a>>>),
162	TextArray(TextArray<'a>),
163	Bytea(&'a [u8]),
164}
165
166impl<'a> ColumnData<'a> {
167	/*pub fn text(s: &'static str) -> Self {
168		Self::Text(s)
169	}
170
171	pub fn unwrap_text(self) -> &'a str {
172		match self {
173			Self::Text(t) => t,
174			_ => panic!("could not unwrap text")
175		}
176	}*/
177
178	#[inline(always)]
179	pub fn is_null(&self) -> bool {
180		matches!(self, Self::Option(None))
181	}
182
183	pub fn unwrap_text(self) -> Text<'a> {
184		match self {
185			Self::Text(t) => t,
186			_ => panic!("could not unwrap text"),
187		}
188	}
189}
190
191#[cfg(feature = "connect")]
192mod impl_postgres {
193
194	use super::*;
195
196	use std::error::Error;
197
198	use bytes::BytesMut;
199
200	use fallible_iterator::FallibleIterator;
201
202	use postgres_types::{accepts, to_sql_checked};
203	use postgres_types::{FromSql, IsNull, Kind as PostgresKind, ToSql, Type};
204
205	use postgres_protocol::types as ty;
206	use postgres_protocol::IsNull as ProtIsNull;
207
208	type BoxedError = Box<dyn Error + Send + Sync + 'static>;
209
210	macro_rules! accepts {
211		() => {
212			fn accepts(ty: &Type) -> bool {
213				match ty {
214					&Type::BOOL
215					| &Type::BPCHAR
216					| &Type::VARCHAR
217					| &Type::TEXT
218					| &Type::DATE
219					| &Type::TIMESTAMP
220					| &Type::FLOAT8
221					| &Type::FLOAT4
222					| &Type::INT8
223					| &Type::INT4
224					| &Type::INT2
225					| &Type::BYTEA
226					| &Type::JSON => true,
227					t => match t.kind() {
228						PostgresKind::Array(Type::TEXT) => true,
229						_ => false,
230					},
231				}
232			}
233		};
234	}
235
236	fn text_array_to_sql<'a, I, T, F>(
237		len: usize,
238		elements: I,
239		f: F,
240		buf: &mut BytesMut,
241	) -> Result<(), BoxedError>
242	where
243		I: IntoIterator<Item = T>,
244		F: Fn(T) -> &'a str,
245	{
246		let len = i32::try_from(len).map_err(|_| "cannot convert i16 to u8")?;
247		let dimension = ty::ArrayDimension {
248			len,
249			lower_bound: 1,
250		};
251
252		let text_oid = Type::TEXT.oid();
253
254		ty::array_to_sql(
255			Some(dimension),
256			text_oid,
257			elements,
258			|e, buf| {
259				ty::text_to_sql(f(e), buf);
260				Ok(ProtIsNull::No)
261			},
262			buf,
263		)
264	}
265
266	impl<'a> ToSql for ColumnData<'a> {
267		fn to_sql(
268			&self,
269			_ty: &Type,
270			out: &mut BytesMut,
271		) -> Result<IsNull, BoxedError> {
272			match self {
273				ColumnData::Boolean(v) => ty::bool_to_sql(*v, out),
274				ColumnData::Text(v) => ty::text_to_sql(v.as_str(), out),
275				ColumnData::Date(v) => ty::date_to_sql(*v, out),
276				ColumnData::Timestamp(v) => ty::timestamp_to_sql(*v, out),
277				ColumnData::F64(v) => ty::float8_to_sql(*v, out),
278				ColumnData::F32(v) => ty::float4_to_sql(*v, out),
279				ColumnData::I64(v) => ty::int8_to_sql(*v, out),
280				ColumnData::I32(v) => ty::int4_to_sql(*v, out),
281				ColumnData::I16(v) => ty::int2_to_sql(*v, out),
282				ColumnData::Option(o) => match o {
283					Some(v) => return v.to_sql(_ty, out),
284					None => return Ok(IsNull::Yes),
285				},
286				ColumnData::Bytea(v) => ty::bytea_to_sql(v, out),
287				ColumnData::TextArray(v) => match v.to_slice_state() {
288					SliceState::Owned(o) => text_array_to_sql(
289						o.len(),
290						o.iter(),
291						|v| v.as_str(),
292						out,
293					)?,
294					SliceState::Borrowed(b) => {
295						text_array_to_sql(b.len(), b.iter(), |v| v, out)?
296					}
297				},
298				//ColumnData::TextArray(v) => text_array_to_sql(v.len(), v, out)?,
299				//ColumnData::TextVecString(v) => text_array_to_sql(v.len(), v, out)?,
300			};
301			Ok(IsNull::No)
302		}
303
304		accepts!();
305
306		to_sql_checked!();
307	}
308
309	impl<'a> FromSql<'a> for ColumnData<'a> {
310		fn from_sql(ty: &Type, raw: &'a [u8]) -> Result<Self, BoxedError> {
311			Ok(match ty {
312				&Type::BOOL => Self::Boolean(ty::bool_from_sql(raw)?),
313				&Type::BPCHAR | &Type::VARCHAR | &Type::TEXT | &Type::JSON => {
314					Self::Text(ty::text_from_sql(raw)?.into())
315				}
316				&Type::DATE => Self::Date(ty::date_from_sql(raw)?),
317				&Type::TIMESTAMP => {
318					Self::Timestamp(ty::timestamp_from_sql(raw)?)
319				}
320				&Type::FLOAT8 => Self::F64(ty::float8_from_sql(raw)?),
321				&Type::FLOAT4 => Self::F32(ty::float4_from_sql(raw)?),
322				&Type::INT8 => Self::I64(ty::int8_from_sql(raw)?),
323				&Type::INT4 => Self::I32(ty::int4_from_sql(raw)?),
324				&Type::INT2 => Self::I16(ty::int2_from_sql(raw)?),
325				&Type::BYTEA => Self::Bytea(ty::bytea_from_sql(raw)),
326				// &Type::TEXTARRAY
327				t => {
328					match t.kind() {
329						PostgresKind::Array(Type::TEXT) => {}
330						_ => return Err("type not recognized".into()),
331					};
332
333					let array = ty::array_from_sql(raw)?;
334					if array.dimensions().count()? > 1 {
335						return Err("array contains too many dimensions".into());
336					}
337
338					if array.element_type() != Type::TEXT.oid() {
339						return Err(
340							"expected array with TEXT AS Element".into()
341						);
342					}
343
344					let mut values = vec![];
345					let mut array = array.values();
346					while let Some(buf) = array.next()? {
347						match buf {
348							Some(buf) => values.push(ty::text_from_sql(buf)?),
349							None => {
350								return Err("array items cannot be null".into())
351							}
352						}
353					}
354
355					Self::TextArray(values.into())
356				}
357			})
358		}
359
360		fn from_sql_null(
361			_: &Type,
362		) -> Result<Self, Box<dyn Error + Sync + Send>> {
363			Ok(Self::Option(None))
364		}
365
366		accepts!();
367	}
368}