1pub mod numeric;
6pub mod temporal;
7
8pub use numeric::Numeric;
9pub use temporal::{Date, Time, Timestamp};
10
11use crate::protocol::types::{decode_json, decode_jsonb, decode_text_array, decode_uuid, oid};
12
13#[derive(Debug, Clone)]
15pub enum TypeError {
16 UnexpectedOid {
18 expected: &'static str,
20 got: u32,
22 },
23 InvalidData(String),
25 UnexpectedNull,
27}
28
29impl std::fmt::Display for TypeError {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 TypeError::UnexpectedOid { expected, got } => {
33 write!(f, "Expected {} type, got OID {}", expected, got)
34 }
35 TypeError::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
36 TypeError::UnexpectedNull => write!(f, "Unexpected NULL value"),
37 }
38 }
39}
40
41impl std::error::Error for TypeError {}
42
43pub trait FromPg: Sized {
45 fn from_pg(bytes: &[u8], oid: u32, format: i16) -> Result<Self, TypeError>;
51}
52
53pub trait ToPg {
55 fn to_pg(&self) -> (Vec<u8>, u32, i16);
58}
59
60impl FromPg for String {
63 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
64 std::str::from_utf8(bytes)
65 .map(str::to_owned)
66 .map_err(|e| TypeError::InvalidData(format!("Invalid UTF-8: {}", e)))
67 }
68}
69
70impl ToPg for String {
71 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
72 (self.as_bytes().to_vec(), oid::TEXT, 0)
73 }
74}
75
76impl ToPg for &str {
77 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
78 (self.as_bytes().to_vec(), oid::TEXT, 0)
79 }
80}
81
82impl FromPg for i32 {
85 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
86 if format == 1 {
87 if bytes.len() != 4 {
89 return Err(TypeError::InvalidData(
90 "Expected 4 bytes for i32".to_string(),
91 ));
92 }
93 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
94 } else {
95 std::str::from_utf8(bytes)
97 .map_err(|e| TypeError::InvalidData(e.to_string()))?
98 .parse()
99 .map_err(|e| TypeError::InvalidData(format!("Invalid i32: {}", e)))
100 }
101 }
102}
103
104impl ToPg for i32 {
105 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
106 (self.to_be_bytes().to_vec(), oid::INT4, 1)
107 }
108}
109
110impl FromPg for i64 {
111 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
112 if format == 1 {
113 if bytes.len() != 8 {
115 return Err(TypeError::InvalidData(
116 "Expected 8 bytes for i64".to_string(),
117 ));
118 }
119 Ok(i64::from_be_bytes([
120 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
121 ]))
122 } else {
123 std::str::from_utf8(bytes)
125 .map_err(|e| TypeError::InvalidData(e.to_string()))?
126 .parse()
127 .map_err(|e| TypeError::InvalidData(format!("Invalid i64: {}", e)))
128 }
129 }
130}
131
132impl ToPg for i64 {
133 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
134 (self.to_be_bytes().to_vec(), oid::INT8, 1)
135 }
136}
137
138impl FromPg for f64 {
141 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
142 if format == 1 {
143 if bytes.len() != 8 {
145 return Err(TypeError::InvalidData(
146 "Expected 8 bytes for f64".to_string(),
147 ));
148 }
149 Ok(f64::from_be_bytes([
150 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
151 ]))
152 } else {
153 std::str::from_utf8(bytes)
155 .map_err(|e| TypeError::InvalidData(e.to_string()))?
156 .parse()
157 .map_err(|e| TypeError::InvalidData(format!("Invalid f64: {}", e)))
158 }
159 }
160}
161
162impl ToPg for f64 {
163 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
164 (self.to_be_bytes().to_vec(), oid::FLOAT8, 1)
165 }
166}
167
168impl FromPg for bool {
171 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
172 if format == 1 {
173 Ok(bytes.first().map(|b| *b != 0).unwrap_or(false))
175 } else {
176 match bytes.first() {
178 Some(b't') | Some(b'T') | Some(b'1') => Ok(true),
179 Some(b'f') | Some(b'F') | Some(b'0') => Ok(false),
180 _ => Err(TypeError::InvalidData("Invalid boolean".to_string())),
181 }
182 }
183 }
184}
185
186impl ToPg for bool {
187 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
188 (vec![if *self { 1 } else { 0 }], oid::BOOL, 1)
189 }
190}
191
192#[derive(Debug, Clone, PartialEq)]
196pub struct Uuid(pub String);
197
198impl FromPg for Uuid {
199 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
200 if oid_val != oid::UUID {
201 return Err(TypeError::UnexpectedOid {
202 expected: "uuid",
203 got: oid_val,
204 });
205 }
206
207 if format == 1 && bytes.len() == 16 {
208 decode_uuid(bytes).map(Uuid).map_err(TypeError::InvalidData)
210 } else {
211 std::str::from_utf8(bytes)
213 .map(str::to_owned)
214 .map(Uuid)
215 .map_err(|e| TypeError::InvalidData(e.to_string()))
216 }
217 }
218}
219
220impl ToPg for Uuid {
221 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
222 (self.0.as_bytes().to_vec(), oid::UUID, 0)
224 }
225}
226
227#[cfg(feature = "uuid")]
228impl FromPg for uuid::Uuid {
229 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
230 let wrapped = Uuid::from_pg(bytes, oid_val, format)?;
231 uuid::Uuid::parse_str(&wrapped.0)
232 .map_err(|e| TypeError::InvalidData(format!("Invalid UUID: {}", e)))
233 }
234}
235
236#[cfg(feature = "uuid")]
237impl ToPg for uuid::Uuid {
238 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
239 (self.as_bytes().to_vec(), oid::UUID, 1)
240 }
241}
242
243fn from_utf8_string(bytes: &[u8]) -> Result<String, TypeError> {
246 std::str::from_utf8(bytes)
247 .map(|s| s.to_string())
248 .map_err(|e| TypeError::InvalidData(e.to_string()))
249}
250
251fn decode_inet_like_binary(bytes: &[u8], force_prefix: bool) -> Result<String, TypeError> {
252 if bytes.len() < 4 {
259 return Err(TypeError::InvalidData(
260 "inet/cidr binary payload too short".to_string(),
261 ));
262 }
263
264 let family = bytes[0];
265 let bits = bytes[1];
266 let is_cidr = bytes[2];
267 let addr_len = bytes[3] as usize;
268
269 if bytes.len() != 4 + addr_len {
270 return Err(TypeError::InvalidData(
271 "inet/cidr binary payload length mismatch".to_string(),
272 ));
273 }
274
275 let addr = &bytes[4..];
276 match family {
277 2 => {
278 if addr_len > 4 {
279 return Err(TypeError::InvalidData(
280 "invalid IPv4 inet/cidr address length".to_string(),
281 ));
282 }
283 let mut full = [0u8; 4];
284 full[..addr_len].copy_from_slice(addr);
285 let ip = std::net::Ipv4Addr::from(full);
286 let include_prefix = force_prefix || is_cidr != 0 || bits != 32;
287 if include_prefix {
288 Ok(format!("{}/{}", ip, bits))
289 } else {
290 Ok(ip.to_string())
291 }
292 }
293 3 => {
294 if addr_len > 16 {
295 return Err(TypeError::InvalidData(
296 "invalid IPv6 inet/cidr address length".to_string(),
297 ));
298 }
299 let mut full = [0u8; 16];
300 full[..addr_len].copy_from_slice(addr);
301 let ip = std::net::Ipv6Addr::from(full);
302 let include_prefix = force_prefix || is_cidr != 0 || bits != 128;
303 if include_prefix {
304 Ok(format!("{}/{}", ip, bits))
305 } else {
306 Ok(ip.to_string())
307 }
308 }
309 _ => Err(TypeError::InvalidData(format!(
310 "unsupported inet/cidr address family: {}",
311 family
312 ))),
313 }
314}
315
316#[derive(Debug, Clone, PartialEq, Eq)]
318pub struct Inet(pub String);
319
320impl Inet {
321 pub fn new(s: impl Into<String>) -> Self {
323 Self(s.into())
324 }
325
326 pub fn as_str(&self) -> &str {
328 &self.0
329 }
330}
331
332impl FromPg for Inet {
333 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
334 if oid_val != oid::INET {
335 return Err(TypeError::UnexpectedOid {
336 expected: "inet",
337 got: oid_val,
338 });
339 }
340
341 let s = if format == 1 {
342 decode_inet_like_binary(bytes, false)?
343 } else {
344 from_utf8_string(bytes)?
345 };
346 Ok(Inet(s))
347 }
348}
349
350impl ToPg for Inet {
351 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
352 (self.0.as_bytes().to_vec(), oid::INET, 0)
353 }
354}
355
356#[derive(Debug, Clone, PartialEq, Eq)]
358pub struct Cidr(pub String);
359
360impl Cidr {
361 pub fn new(s: impl Into<String>) -> Self {
363 Self(s.into())
364 }
365
366 pub fn as_str(&self) -> &str {
368 &self.0
369 }
370}
371
372impl FromPg for Cidr {
373 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
374 if oid_val != oid::CIDR {
375 return Err(TypeError::UnexpectedOid {
376 expected: "cidr",
377 got: oid_val,
378 });
379 }
380
381 let s = if format == 1 {
382 decode_inet_like_binary(bytes, true)?
383 } else {
384 from_utf8_string(bytes)?
385 };
386 Ok(Cidr(s))
387 }
388}
389
390impl ToPg for Cidr {
391 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
392 (self.0.as_bytes().to_vec(), oid::CIDR, 0)
393 }
394}
395
396#[derive(Debug, Clone, PartialEq, Eq)]
398pub struct MacAddr(pub String);
399
400impl MacAddr {
401 pub fn new(s: impl Into<String>) -> Self {
403 Self(s.into())
404 }
405
406 pub fn as_str(&self) -> &str {
408 &self.0
409 }
410}
411
412impl FromPg for MacAddr {
413 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
414 if oid_val != oid::MACADDR {
415 return Err(TypeError::UnexpectedOid {
416 expected: "macaddr",
417 got: oid_val,
418 });
419 }
420
421 let s = if format == 1 {
422 if bytes.len() != 6 {
423 return Err(TypeError::InvalidData(
424 "Expected 6 bytes for macaddr".to_string(),
425 ));
426 }
427 format!(
428 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
429 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
430 )
431 } else {
432 from_utf8_string(bytes)?
433 };
434
435 Ok(MacAddr(s))
436 }
437}
438
439impl ToPg for MacAddr {
440 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
441 (self.0.as_bytes().to_vec(), oid::MACADDR, 0)
442 }
443}
444
445#[derive(Debug, Clone, PartialEq)]
449pub struct Json(pub String);
450
451impl FromPg for Json {
452 fn from_pg(bytes: &[u8], oid_val: u32, _format: i16) -> Result<Self, TypeError> {
453 let json_str = if oid_val == oid::JSONB {
454 decode_jsonb(bytes).map_err(TypeError::InvalidData)?
455 } else {
456 decode_json(bytes).map_err(TypeError::InvalidData)?
457 };
458 Ok(Json(json_str))
459 }
460}
461
462impl ToPg for Json {
463 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
464 let mut buf = Vec::with_capacity(1 + self.0.len());
466 buf.push(1); buf.extend_from_slice(self.0.as_bytes());
468 (buf, oid::JSONB, 1)
469 }
470}
471
472impl FromPg for Vec<String> {
475 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
476 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
477 Ok(decode_text_array(s))
478 }
479}
480
481impl FromPg for Vec<i64> {
482 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
483 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
484 crate::protocol::types::decode_int_array(s).map_err(TypeError::InvalidData)
485 }
486}
487
488impl<T: FromPg> FromPg for Option<T> {
491 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
492 Ok(Some(T::from_pg(bytes, oid_val, format)?))
494 }
495}
496
497impl FromPg for Vec<u8> {
500 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
501 Ok(bytes.to_vec())
502 }
503}
504
505impl ToPg for Vec<u8> {
506 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
507 (self.clone(), oid::BYTEA, 1)
508 }
509}
510
511impl ToPg for &[u8] {
512 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
513 (self.to_vec(), oid::BYTEA, 1)
514 }
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520
521 #[test]
522 fn test_string_from_pg() {
523 let result = String::from_pg(b"hello", oid::TEXT, 0).unwrap();
524 assert_eq!(result, "hello");
525 }
526
527 #[test]
528 fn test_i32_from_pg_text() {
529 let result = i32::from_pg(b"42", oid::INT4, 0).unwrap();
530 assert_eq!(result, 42);
531 }
532
533 #[test]
534 fn test_i32_from_pg_binary() {
535 let bytes = 42i32.to_be_bytes();
536 let result = i32::from_pg(&bytes, oid::INT4, 1).unwrap();
537 assert_eq!(result, 42);
538 }
539
540 #[test]
541 fn test_bool_from_pg() {
542 assert!(bool::from_pg(b"t", oid::BOOL, 0).unwrap());
543 assert!(!bool::from_pg(b"f", oid::BOOL, 0).unwrap());
544 assert!(bool::from_pg(&[1], oid::BOOL, 1).unwrap());
545 assert!(!bool::from_pg(&[0], oid::BOOL, 1).unwrap());
546 }
547
548 #[test]
549 fn test_uuid_from_pg_binary() {
550 let uuid_bytes: [u8; 16] = [
551 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
552 0x00, 0x00,
553 ];
554 let result = Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
555 assert_eq!(result.0, "550e8400-e29b-41d4-a716-446655440000");
556 }
557
558 #[cfg(feature = "uuid")]
559 #[test]
560 fn test_std_uuid_from_pg_binary() {
561 let uuid_bytes: [u8; 16] = [
562 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
563 0x00, 0x00,
564 ];
565 let result = uuid::Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
566 assert_eq!(result.to_string(), "550e8400-e29b-41d4-a716-446655440000");
567 }
568
569 #[test]
570 fn test_inet_from_pg_text() {
571 let inet = Inet::from_pg(b"10.0.0.1", oid::INET, 0).unwrap();
572 assert_eq!(inet.0, "10.0.0.1");
573 }
574
575 #[test]
576 fn test_inet_from_pg_binary_ipv4() {
577 let bytes = [2u8, 32, 0, 4, 10, 1, 2, 3];
579 let inet = Inet::from_pg(&bytes, oid::INET, 1).unwrap();
580 assert_eq!(inet.0, "10.1.2.3");
581 }
582
583 #[test]
584 fn test_cidr_from_pg_binary_ipv4() {
585 let bytes = [2u8, 24, 1, 4, 192, 168, 1, 0];
587 let cidr = Cidr::from_pg(&bytes, oid::CIDR, 1).unwrap();
588 assert_eq!(cidr.0, "192.168.1.0/24");
589 }
590
591 #[test]
592 fn test_macaddr_from_pg_binary() {
593 let bytes = [0x08u8, 0x00, 0x2b, 0x01, 0x02, 0x03];
594 let mac = MacAddr::from_pg(&bytes, oid::MACADDR, 1).unwrap();
595 assert_eq!(mac.0, "08:00:2b:01:02:03");
596 }
597
598 #[test]
599 fn test_network_types_to_pg_oids() {
600 let inet = Inet::new("10.0.0.0/8");
601 let (_, inet_oid, inet_format) = inet.to_pg();
602 assert_eq!(inet_oid, oid::INET);
603 assert_eq!(inet_format, 0);
604
605 let cidr = Cidr::new("10.0.0.0/8");
606 let (_, cidr_oid, cidr_format) = cidr.to_pg();
607 assert_eq!(cidr_oid, oid::CIDR);
608 assert_eq!(cidr_format, 0);
609
610 let mac = MacAddr::new("08:00:2b:01:02:03");
611 let (_, mac_oid, mac_format) = mac.to_pg();
612 assert_eq!(mac_oid, oid::MACADDR);
613 assert_eq!(mac_format, 0);
614 }
615}