1use byteorder::ReadBytesExt;
2
3#[inline]
4pub fn not_null<T>(raw: Option<T>) -> crate::Result<T> {
5 raw.ok_or(crate::Error::NotNull)
6}
7
8macro_rules! read {
9 ($fn:ident, $ty:ty) => {
10 #[inline]
11 #[allow(dead_code)]
12 pub fn $fn(buf: &mut &[u8]) -> crate::Result<$ty> {
13 let n = buf.$fn::<byteorder::BigEndian>()?;
14
15 Ok(n)
16 }
17 };
18}
19
20read!(read_i16, i16);
21read!(read_i32, i32);
22read!(read_i64, i64);
23read!(read_f32, f32);
24read!(read_f64, f64);
25read!(read_u32, u32);
26read!(read_u128, u128);
27
28#[inline]
29#[allow(dead_code)]
30pub fn read_i8(buf: &mut &[u8]) -> crate::Result<i8> {
31 let n = buf.read_i8()?;
32
33 Ok(n)
34}
35
36#[inline]
37pub fn read_u8(buf: &mut &[u8]) -> crate::Result<u8> {
38 let n = buf.read_u8()?;
39
40 Ok(n)
41}
42
43macro_rules! number {
44 ($type:ty, $read:ident) => {
45 impl FromSql for $type {
46 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
47 let mut buf = crate::from_sql::not_null(raw)?;
48 let v = $read(&mut buf)?;
49
50 if !buf.is_empty() {
51 return Err(Self::error(ty, raw));
52 }
53
54 Ok(v)
55 }
56
57 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
58 crate::from_sql::not_null(raw)?
59 .parse()
60 .map_err(|_| Self::error(ty, raw))
61 }
62 }
63 };
64}
65
66pub trait FromSql: Sized {
70 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self>;
78
79 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self>;
87
88 fn from_sql(
92 ty: &crate::pq::Type,
93 format: crate::pq::Format,
94 raw: Option<&[u8]>,
95 ) -> crate::Result<Self> {
96 match format {
97 crate::pq::Format::Binary => Self::from_binary(ty, raw),
98 crate::pq::Format::Text => {
99 let text = raw.map(|x| String::from_utf8(x.to_vec())).transpose()?;
100
101 Self::from_text(ty, text.as_deref())
102 }
103 }
104 }
105
106 fn error<T: std::fmt::Debug>(pg_type: &crate::pq::Type, raw: T) -> crate::Error {
107 crate::Error::FromSql {
108 pg_type: pg_type.clone(),
109 rust_type: std::any::type_name::<Self>().to_string(),
110 value: format!("{raw:?}"),
111 }
112 }
113}
114
115number!(f32, read_f32);
116number!(f64, read_f64);
117number!(i16, read_i16);
118number!(i32, read_i32);
119number!(i64, read_i64);
120
121impl FromSql for u16 {
122 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
123 i32::from_text(ty, raw).map(|x| x as u16)
124 }
125
126 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
127 let raw = raw.map(|x| {
128 let mut vec = vec![0; 4 - x.len()];
129 vec.extend_from_slice(x);
130 vec
131 });
132
133 i32::from_binary(ty, raw.as_deref()).map(|x| x as u16)
134 }
135}
136
137impl FromSql for u32 {
138 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
139 i64::from_text(ty, raw).map(|x| x as u32)
140 }
141
142 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
143 let raw = raw.map(|x| {
144 let mut vec = vec![0; 8 - x.len()];
145 vec.extend_from_slice(x);
146 vec
147 });
148
149 i64::from_binary(ty, raw.as_deref()).map(|x| x as u32)
150 }
151}
152
153impl FromSql for usize {
154 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
155 not_null(raw)?.parse().map_err(|_| Self::error(ty, raw))
156 }
157
158 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
159 let mut buf = not_null(raw)?;
160 #[cfg(target_pointer_width = "64")]
161 let v = buf.read_u64::<byteorder::BigEndian>()?;
162 #[cfg(target_pointer_width = "32")]
163 let v = buf.read_u32::<byteorder::BigEndian>()?;
164
165 if !buf.is_empty() {
166 return Err(Self::error(ty, raw));
167 }
168
169 Ok(v as usize)
170 }
171}
172
173impl FromSql for bool {
174 fn from_text(_: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
175 Ok(not_null(raw)? == "t")
176 }
177
178 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
179 let buf = not_null(raw)?;
180 if buf.len() != 1 {
181 return Err(Self::error(ty, raw));
182 }
183
184 Ok(not_null(raw)?[0] != 0)
185 }
186}
187
188impl<T: FromSql> FromSql for Option<T> {
189 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
190 match raw {
191 Some(_) => Ok(Some(T::from_text(ty, raw)?)),
192 None => Ok(None),
193 }
194 }
195
196 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
197 match raw {
198 Some(_) => Ok(Some(T::from_binary(ty, raw)?)),
199 None => Ok(None),
200 }
201 }
202}
203
204impl FromSql for char {
205 fn from_text(ty: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
206 not_null(raw)?
207 .chars()
208 .next()
209 .ok_or_else(|| Self::error(ty, raw))
210 }
211
212 fn from_binary(ty: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
213 let c = String::from_binary(ty, raw)?;
214
215 c.chars().next().ok_or_else(|| Self::error(ty, raw))
216 }
217}
218
219impl FromSql for String {
220 fn from_text(_: &crate::pq::Type, raw: Option<&str>) -> crate::Result<Self> {
221 Ok(not_null(raw)?.to_string())
222 }
223
224 fn from_binary(_: &crate::pq::Type, raw: Option<&[u8]>) -> crate::Result<Self> {
225 String::from_utf8(not_null(raw)?.to_vec()).map_err(Into::into)
226 }
227}
228
229impl FromSql for () {
230 fn from_text(_: &crate::pq::Type, _: Option<&str>) -> crate::Result<Self> {
231 Ok(())
232 }
233
234 fn from_binary(_: &crate::pq::Type, _: Option<&[u8]>) -> crate::Result<Self> {
235 Ok(())
236 }
237}
238
239#[cfg(test)]
240mod test {
241 crate::sql_test!(float4, f32, [("1.", 1.), ("-1.", -1.), ("2.1", 2.1)]);
242
243 crate::sql_test!(float8, f64, [("1.", 1.), ("-1.", -1.), ("2.1", 2.1)]);
244
245 crate::sql_test!(
246 int2,
247 i16,
248 [
249 (i16::MAX.to_string().as_str(), i16::MAX),
250 ("1", 1),
251 ("0", 0),
252 ("-1", -1),
253 ]
254 );
255
256 crate::sql_test!(
257 int,
258 u16,
259 [
260 (u16::MAX.to_string().as_str(), u16::MAX),
261 ("1", 1),
262 ("0", 0)
263 ]
264 );
265
266 crate::sql_test!(
267 int4,
268 i32,
269 [
270 (i32::MAX.to_string().as_str(), i32::MAX),
271 ("1", 1),
272 ("0", 0),
273 ("-1", -1),
274 ]
275 );
276
277 crate::sql_test!(
278 bigint,
279 u32,
280 [
281 (u32::MAX.to_string().as_str(), u32::MAX),
282 ("1", 1),
283 ("0", 0)
284 ]
285 );
286
287 crate::sql_test!(
288 int8,
289 i64,
290 [
291 (i64::MAX.to_string().as_str(), i64::MAX),
292 ("1", 1),
293 ("0", 0),
294 ("-1", -1),
295 ]
296 );
297
298 crate::sql_test!(oid, crate::pq::Oid, [("1", 1)]);
299
300 crate::sql_test!(
301 bool,
302 bool,
303 [
304 ("'t'", true),
305 ("'f'", false),
306 ("true", true),
307 ("false", false),
308 ]
309 );
310
311 crate::sql_test!(char, char, [("'f'", 'f'), ("'('", '(')]);
312
313 crate::sql_test!(varchar, Option<String>, [("null", None::<String>)]);
314
315 crate::sql_test!(
316 text,
317 String,
318 [("'foo'", "foo".to_string()), ("''", "".to_string())]
319 );
320
321 crate::sql_test!(us_postal_code, String, [("'12345'", "12345".to_string()),]);
322
323 crate::sql_test!(unknown, (), [("null", ())]);
324
325 #[derive(elephantry_derive::Enum, Debug, PartialEq)]
326 enum Mood {
327 Sad,
328 Ok,
329 Happy,
330 }
331
332 crate::sql_test!(
333 mood,
334 super::Mood,
335 [
336 ("'Sad'", super::Mood::Sad),
337 ("'Ok'", super::Mood::Ok),
338 ("'Happy'", super::Mood::Happy),
339 ]
340 );
341
342 #[derive(elephantry_derive::Composite, Debug, PartialEq)]
343 struct CompFoo {
344 f1: i32,
345 f2: String,
346 }
347
348 impl crate::entity::Simple for CompFoo {}
349
350 crate::sql_test!(
351 compfoo,
352 super::CompFoo,
353 [(
354 "'(1,foo)'",
355 super::CompFoo {
356 f1: 1,
357 f2: "foo".to_string()
358 }
359 )]
360 );
361}