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_uuid, oid, try_decode_text_array};
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_val: u32, format: i16) -> Result<Self, TypeError> {
86 if format == 1 {
87 match oid_val {
88 oid::INT2 => {
89 if bytes.len() != 2 {
90 return Err(TypeError::InvalidData(
91 "Expected 2 bytes for int2".to_string(),
92 ));
93 }
94 Ok(i16::from_be_bytes([bytes[0], bytes[1]]) as i32)
95 }
96 oid::INT4 => {
97 if bytes.len() != 4 {
98 return Err(TypeError::InvalidData(
99 "Expected 4 bytes for int4".to_string(),
100 ));
101 }
102 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
103 }
104 _ => Err(TypeError::UnexpectedOid {
105 expected: "int2/int4",
106 got: oid_val,
107 }),
108 }
109 } else {
110 std::str::from_utf8(bytes)
112 .map_err(|e| TypeError::InvalidData(e.to_string()))?
113 .parse()
114 .map_err(|e| TypeError::InvalidData(format!("Invalid i32: {}", e)))
115 }
116 }
117}
118
119impl ToPg for i32 {
120 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
121 (self.to_be_bytes().to_vec(), oid::INT4, 1)
122 }
123}
124
125impl FromPg for i64 {
126 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
127 if format == 1 {
128 match oid_val {
129 oid::INT2 => {
130 if bytes.len() != 2 {
131 return Err(TypeError::InvalidData(
132 "Expected 2 bytes for int2".to_string(),
133 ));
134 }
135 Ok(i16::from_be_bytes([bytes[0], bytes[1]]) as i64)
136 }
137 oid::INT4 => {
138 if bytes.len() != 4 {
139 return Err(TypeError::InvalidData(
140 "Expected 4 bytes for int4".to_string(),
141 ));
142 }
143 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as i64)
144 }
145 oid::INT8 => {
146 if bytes.len() != 8 {
147 return Err(TypeError::InvalidData(
148 "Expected 8 bytes for int8".to_string(),
149 ));
150 }
151 Ok(i64::from_be_bytes([
152 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6],
153 bytes[7],
154 ]))
155 }
156 _ => Err(TypeError::UnexpectedOid {
157 expected: "int2/int4/int8",
158 got: oid_val,
159 }),
160 }
161 } else {
162 std::str::from_utf8(bytes)
164 .map_err(|e| TypeError::InvalidData(e.to_string()))?
165 .parse()
166 .map_err(|e| TypeError::InvalidData(format!("Invalid i64: {}", e)))
167 }
168 }
169}
170
171impl ToPg for i64 {
172 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
173 (self.to_be_bytes().to_vec(), oid::INT8, 1)
174 }
175}
176
177impl FromPg for f64 {
180 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
181 if format == 1 {
182 match oid_val {
183 oid::FLOAT4 => {
184 if bytes.len() != 4 {
185 return Err(TypeError::InvalidData(
186 "Expected 4 bytes for float4".to_string(),
187 ));
188 }
189 Ok(f32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as f64)
190 }
191 oid::FLOAT8 => {
192 if bytes.len() != 8 {
193 return Err(TypeError::InvalidData(
194 "Expected 8 bytes for float8".to_string(),
195 ));
196 }
197 Ok(f64::from_be_bytes([
198 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6],
199 bytes[7],
200 ]))
201 }
202 _ => Err(TypeError::UnexpectedOid {
203 expected: "float4/float8",
204 got: oid_val,
205 }),
206 }
207 } else {
208 std::str::from_utf8(bytes)
210 .map_err(|e| TypeError::InvalidData(e.to_string()))?
211 .parse()
212 .map_err(|e| TypeError::InvalidData(format!("Invalid f64: {}", e)))
213 }
214 }
215}
216
217impl ToPg for f64 {
218 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
219 (self.to_be_bytes().to_vec(), oid::FLOAT8, 1)
220 }
221}
222
223impl FromPg for bool {
226 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
227 if format == 1 {
228 if bytes.len() != 1 {
229 return Err(TypeError::InvalidData(
230 "Expected 1 byte for boolean".to_string(),
231 ));
232 }
233 match bytes[0] {
234 0 => Ok(false),
235 1 => Ok(true),
236 value => Err(TypeError::InvalidData(format!(
237 "Invalid binary boolean: {}",
238 value
239 ))),
240 }
241 } else {
242 match std::str::from_utf8(bytes)
244 .map_err(|e| TypeError::InvalidData(e.to_string()))?
245 .trim()
246 {
247 "t" | "T" | "true" | "TRUE" | "1" => Ok(true),
248 "f" | "F" | "false" | "FALSE" | "0" => Ok(false),
249 _ => Err(TypeError::InvalidData("Invalid boolean".to_string())),
250 }
251 }
252 }
253}
254
255impl ToPg for bool {
256 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
257 (vec![if *self { 1 } else { 0 }], oid::BOOL, 1)
258 }
259}
260
261#[derive(Debug, Clone, PartialEq)]
265pub struct Uuid(pub String);
266
267impl FromPg for Uuid {
268 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
269 if oid_val != oid::UUID {
270 return Err(TypeError::UnexpectedOid {
271 expected: "uuid",
272 got: oid_val,
273 });
274 }
275
276 if format == 1 {
277 if bytes.len() != 16 {
278 return Err(TypeError::InvalidData(
279 "Expected 16 bytes for UUID".to_string(),
280 ));
281 }
282 decode_uuid(bytes).map(Uuid).map_err(TypeError::InvalidData)
283 } else {
284 std::str::from_utf8(bytes)
286 .map(str::to_owned)
287 .map(Uuid)
288 .map_err(|e| TypeError::InvalidData(e.to_string()))
289 }
290 }
291}
292
293impl ToPg for Uuid {
294 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
295 (self.0.as_bytes().to_vec(), oid::UUID, 0)
297 }
298}
299
300#[cfg(feature = "uuid")]
301impl FromPg for uuid::Uuid {
302 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
303 let wrapped = Uuid::from_pg(bytes, oid_val, format)?;
304 uuid::Uuid::parse_str(&wrapped.0)
305 .map_err(|e| TypeError::InvalidData(format!("Invalid UUID: {}", e)))
306 }
307}
308
309#[cfg(feature = "uuid")]
310impl ToPg for uuid::Uuid {
311 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
312 (self.as_bytes().to_vec(), oid::UUID, 1)
313 }
314}
315
316fn from_utf8_string(bytes: &[u8]) -> Result<String, TypeError> {
319 std::str::from_utf8(bytes)
320 .map(|s| s.to_string())
321 .map_err(|e| TypeError::InvalidData(e.to_string()))
322}
323
324fn decode_inet_like_binary(bytes: &[u8], force_prefix: bool) -> Result<String, TypeError> {
325 if bytes.len() < 4 {
332 return Err(TypeError::InvalidData(
333 "inet/cidr binary payload too short".to_string(),
334 ));
335 }
336
337 let family = bytes[0];
338 let bits = bytes[1];
339 let is_cidr = bytes[2];
340 let addr_len = bytes[3] as usize;
341
342 if bytes.len() != 4 + addr_len {
343 return Err(TypeError::InvalidData(
344 "inet/cidr binary payload length mismatch".to_string(),
345 ));
346 }
347 if is_cidr > 1 {
348 return Err(TypeError::InvalidData(
349 "invalid inet/cidr is_cidr flag".to_string(),
350 ));
351 }
352
353 let addr = &bytes[4..];
354 match family {
355 2 => {
356 if addr_len != 4 {
357 return Err(TypeError::InvalidData(
358 "invalid IPv4 inet/cidr address length".to_string(),
359 ));
360 }
361 if bits > 32 {
362 return Err(TypeError::InvalidData(
363 "invalid IPv4 inet/cidr prefix length".to_string(),
364 ));
365 }
366 let ip = std::net::Ipv4Addr::from([addr[0], addr[1], addr[2], addr[3]]);
367 let include_prefix = force_prefix || is_cidr != 0 || bits != 32;
368 if include_prefix {
369 Ok(format!("{}/{}", ip, bits))
370 } else {
371 Ok(ip.to_string())
372 }
373 }
374 3 => {
375 if addr_len != 16 {
376 return Err(TypeError::InvalidData(
377 "invalid IPv6 inet/cidr address length".to_string(),
378 ));
379 }
380 if bits > 128 {
381 return Err(TypeError::InvalidData(
382 "invalid IPv6 inet/cidr prefix length".to_string(),
383 ));
384 }
385 let mut full = [0u8; 16];
386 full.copy_from_slice(addr);
387 let ip = std::net::Ipv6Addr::from(full);
388 let include_prefix = force_prefix || is_cidr != 0 || bits != 128;
389 if include_prefix {
390 Ok(format!("{}/{}", ip, bits))
391 } else {
392 Ok(ip.to_string())
393 }
394 }
395 _ => Err(TypeError::InvalidData(format!(
396 "unsupported inet/cidr address family: {}",
397 family
398 ))),
399 }
400}
401
402#[derive(Debug, Clone, PartialEq, Eq)]
404pub struct Inet(pub String);
405
406impl Inet {
407 pub fn new(s: impl Into<String>) -> Self {
409 Self(s.into())
410 }
411
412 pub fn as_str(&self) -> &str {
414 &self.0
415 }
416}
417
418impl FromPg for Inet {
419 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
420 if oid_val != oid::INET {
421 return Err(TypeError::UnexpectedOid {
422 expected: "inet",
423 got: oid_val,
424 });
425 }
426
427 let s = if format == 1 {
428 decode_inet_like_binary(bytes, false)?
429 } else {
430 from_utf8_string(bytes)?
431 };
432 Ok(Inet(s))
433 }
434}
435
436impl ToPg for Inet {
437 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
438 (self.0.as_bytes().to_vec(), oid::INET, 0)
439 }
440}
441
442#[derive(Debug, Clone, PartialEq, Eq)]
444pub struct Cidr(pub String);
445
446impl Cidr {
447 pub fn new(s: impl Into<String>) -> Self {
449 Self(s.into())
450 }
451
452 pub fn as_str(&self) -> &str {
454 &self.0
455 }
456}
457
458impl FromPg for Cidr {
459 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
460 if oid_val != oid::CIDR {
461 return Err(TypeError::UnexpectedOid {
462 expected: "cidr",
463 got: oid_val,
464 });
465 }
466
467 let s = if format == 1 {
468 decode_inet_like_binary(bytes, true)?
469 } else {
470 from_utf8_string(bytes)?
471 };
472 Ok(Cidr(s))
473 }
474}
475
476impl ToPg for Cidr {
477 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
478 (self.0.as_bytes().to_vec(), oid::CIDR, 0)
479 }
480}
481
482#[derive(Debug, Clone, PartialEq, Eq)]
484pub struct MacAddr(pub String);
485
486impl MacAddr {
487 pub fn new(s: impl Into<String>) -> Self {
489 Self(s.into())
490 }
491
492 pub fn as_str(&self) -> &str {
494 &self.0
495 }
496}
497
498impl FromPg for MacAddr {
499 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
500 if oid_val != oid::MACADDR {
501 return Err(TypeError::UnexpectedOid {
502 expected: "macaddr",
503 got: oid_val,
504 });
505 }
506
507 let s = if format == 1 {
508 if bytes.len() != 6 {
509 return Err(TypeError::InvalidData(
510 "Expected 6 bytes for macaddr".to_string(),
511 ));
512 }
513 format!(
514 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
515 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
516 )
517 } else {
518 from_utf8_string(bytes)?
519 };
520
521 Ok(MacAddr(s))
522 }
523}
524
525impl ToPg for MacAddr {
526 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
527 (self.0.as_bytes().to_vec(), oid::MACADDR, 0)
528 }
529}
530
531#[derive(Debug, Clone, PartialEq)]
535pub struct Json(pub String);
536
537impl FromPg for Json {
538 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
539 let json_str = match (oid_val, format) {
540 (oid::JSON, _) | (oid::JSONB, 0) => {
541 decode_json(bytes).map_err(TypeError::InvalidData)?
542 }
543 (oid::JSONB, 1) => decode_jsonb(bytes).map_err(TypeError::InvalidData)?,
544 (oid::JSONB, other) => {
545 return Err(TypeError::InvalidData(format!(
546 "Unsupported JSONB format code: {}",
547 other
548 )));
549 }
550 _ => {
551 return Err(TypeError::UnexpectedOid {
552 expected: "json/jsonb",
553 got: oid_val,
554 });
555 }
556 };
557 Ok(Json(json_str))
558 }
559}
560
561impl ToPg for Json {
562 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
563 let mut buf = Vec::with_capacity(1 + self.0.len());
565 buf.push(1); buf.extend_from_slice(self.0.as_bytes());
567 (buf, oid::JSONB, 1)
568 }
569}
570
571impl FromPg for Vec<String> {
574 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
575 if format == 1 {
576 return Err(TypeError::InvalidData(
577 "binary array decoding is not supported for Vec<String>".to_string(),
578 ));
579 }
580 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
581 try_decode_text_array(s).map_err(TypeError::InvalidData)
582 }
583}
584
585impl FromPg for Vec<i64> {
586 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
587 if format == 1 {
588 return Err(TypeError::InvalidData(
589 "binary array decoding is not supported for Vec<i64>".to_string(),
590 ));
591 }
592 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
593 crate::protocol::types::decode_int_array(s).map_err(TypeError::InvalidData)
594 }
595}
596
597impl<T: FromPg> FromPg for Option<T> {
600 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
601 Ok(Some(T::from_pg(bytes, oid_val, format)?))
603 }
604}
605
606fn decode_bytea_hex_text(bytes: &[u8]) -> Result<Vec<u8>, TypeError> {
609 if !bytes.starts_with(br"\x") {
610 return Ok(bytes.to_vec());
611 }
612
613 let hex = &bytes[2..];
614 if !hex.len().is_multiple_of(2) {
615 return Err(TypeError::InvalidData(
616 "Invalid bytea hex text length".to_string(),
617 ));
618 }
619
620 let mut out = Vec::with_capacity(hex.len() / 2);
621 for pair in hex.chunks_exact(2) {
622 let hi = decode_hex_nibble(pair[0])?;
623 let lo = decode_hex_nibble(pair[1])?;
624 out.push((hi << 4) | lo);
625 }
626 Ok(out)
627}
628
629fn decode_hex_nibble(byte: u8) -> Result<u8, TypeError> {
630 match byte {
631 b'0'..=b'9' => Ok(byte - b'0'),
632 b'a'..=b'f' => Ok(byte - b'a' + 10),
633 b'A'..=b'F' => Ok(byte - b'A' + 10),
634 _ => Err(TypeError::InvalidData(format!(
635 "Invalid bytea hex digit: {}",
636 byte as char
637 ))),
638 }
639}
640
641impl FromPg for Vec<u8> {
642 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
643 if format == 0 && oid_val == oid::BYTEA {
644 return decode_bytea_hex_text(bytes);
645 }
646 Ok(bytes.to_vec())
647 }
648}
649
650impl ToPg for Vec<u8> {
651 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
652 (self.clone(), oid::BYTEA, 1)
653 }
654}
655
656impl ToPg for &[u8] {
657 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
658 (self.to_vec(), oid::BYTEA, 1)
659 }
660}
661
662#[cfg(test)]
663mod tests {
664 use super::*;
665
666 #[test]
667 fn test_string_from_pg() {
668 let result = String::from_pg(b"hello", oid::TEXT, 0).unwrap();
669 assert_eq!(result, "hello");
670 }
671
672 #[test]
673 fn test_i32_from_pg_text() {
674 let result = i32::from_pg(b"42", oid::INT4, 0).unwrap();
675 assert_eq!(result, 42);
676 }
677
678 #[test]
679 fn test_i32_from_pg_binary() {
680 let bytes = 42i32.to_be_bytes();
681 let result = i32::from_pg(&bytes, oid::INT4, 1).unwrap();
682 assert_eq!(result, 42);
683 }
684
685 #[test]
686 fn test_i32_from_pg_binary_accepts_int2_oid() {
687 let bytes = 123i16.to_be_bytes();
688 let result = i32::from_pg(&bytes, oid::INT2, 1).unwrap();
689 assert_eq!(result, 123);
690 }
691
692 #[test]
693 fn test_i64_from_pg_binary_accepts_int4_oid() {
694 let bytes = 123_456i32.to_be_bytes();
695 let result = i64::from_pg(&bytes, oid::INT4, 1).unwrap();
696 assert_eq!(result, 123_456);
697 }
698
699 #[test]
700 fn test_f64_from_pg_binary_accepts_float4_oid() {
701 let bytes = 1.5f32.to_be_bytes();
702 let result = f64::from_pg(&bytes, oid::FLOAT4, 1).unwrap();
703 assert_eq!(result, 1.5);
704 }
705
706 #[test]
707 fn test_bool_from_pg() {
708 assert!(bool::from_pg(b"t", oid::BOOL, 0).unwrap());
709 assert!(!bool::from_pg(b"f", oid::BOOL, 0).unwrap());
710 assert!(bool::from_pg(&[1], oid::BOOL, 1).unwrap());
711 assert!(!bool::from_pg(&[0], oid::BOOL, 1).unwrap());
712 }
713
714 #[test]
715 fn test_bool_from_pg_rejects_malformed_values() {
716 assert!(bool::from_pg(&[], oid::BOOL, 1).is_err());
717 assert!(bool::from_pg(&[2], oid::BOOL, 1).is_err());
718 assert!(bool::from_pg(b"trash", oid::BOOL, 0).is_err());
719 assert!(bool::from_pg(b"falsey", oid::BOOL, 0).is_err());
720 }
721
722 #[test]
723 fn test_uuid_from_pg_binary() {
724 let uuid_bytes: [u8; 16] = [
725 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
726 0x00, 0x00,
727 ];
728 let result = Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
729 assert_eq!(result.0, "550e8400-e29b-41d4-a716-446655440000");
730 }
731
732 #[test]
733 fn test_uuid_from_pg_binary_rejects_bad_length() {
734 let err = Uuid::from_pg(b"550e8400-e29b-41d4-a716-446655440000", oid::UUID, 1).unwrap_err();
735
736 assert!(matches!(err, TypeError::InvalidData(msg) if msg.contains("16 bytes")));
737 }
738
739 #[cfg(feature = "uuid")]
740 #[test]
741 fn test_std_uuid_from_pg_binary() {
742 let uuid_bytes: [u8; 16] = [
743 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
744 0x00, 0x00,
745 ];
746 let result = uuid::Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
747 assert_eq!(result.to_string(), "550e8400-e29b-41d4-a716-446655440000");
748 }
749
750 #[test]
751 fn test_inet_from_pg_text() {
752 let inet = Inet::from_pg(b"10.0.0.1", oid::INET, 0).unwrap();
753 assert_eq!(inet.0, "10.0.0.1");
754 }
755
756 #[test]
757 fn test_inet_from_pg_binary_ipv4() {
758 let bytes = [2u8, 32, 0, 4, 10, 1, 2, 3];
760 let inet = Inet::from_pg(&bytes, oid::INET, 1).unwrap();
761 assert_eq!(inet.0, "10.1.2.3");
762 }
763
764 #[test]
765 fn test_bytea_text_hex_decodes_to_bytes() {
766 let bytes = Vec::<u8>::from_pg(br"\x000102ff", oid::BYTEA, 0).unwrap();
767 assert_eq!(bytes, vec![0, 1, 2, 255]);
768 }
769
770 #[test]
771 fn test_binary_arrays_return_explicit_error() {
772 assert!(Vec::<String>::from_pg(b"\0\0\0\0", oid::TEXT_ARRAY, 1).is_err());
773 assert!(Vec::<i64>::from_pg(b"\0\0\0\0", oid::INT8_ARRAY, 1).is_err());
774 }
775
776 #[test]
777 fn test_cidr_from_pg_binary_ipv4() {
778 let bytes = [2u8, 24, 1, 4, 192, 168, 1, 0];
780 let cidr = Cidr::from_pg(&bytes, oid::CIDR, 1).unwrap();
781 assert_eq!(cidr.0, "192.168.1.0/24");
782 }
783
784 #[test]
785 fn test_network_types_reject_malformed_binary_payloads() {
786 assert!(Inet::from_pg(&[2u8, 33, 0, 4, 10, 1, 2, 3], oid::INET, 1).is_err());
787 assert!(Inet::from_pg(&[2u8, 32, 0, 1, 10], oid::INET, 1).is_err());
788 assert!(Inet::from_pg(&[2u8, 32, 2, 4, 10, 1, 2, 3], oid::INET, 1).is_err());
789
790 let mut ipv6 = vec![3u8, 129, 0, 16];
791 ipv6.extend_from_slice(&[0u8; 16]);
792 assert!(Inet::from_pg(&ipv6, oid::INET, 1).is_err());
793 }
794
795 #[test]
796 fn test_macaddr_from_pg_binary() {
797 let bytes = [0x08u8, 0x00, 0x2b, 0x01, 0x02, 0x03];
798 let mac = MacAddr::from_pg(&bytes, oid::MACADDR, 1).unwrap();
799 assert_eq!(mac.0, "08:00:2b:01:02:03");
800 }
801
802 #[test]
803 fn test_network_types_to_pg_oids() {
804 let inet = Inet::new("10.0.0.0/8");
805 let (_, inet_oid, inet_format) = inet.to_pg();
806 assert_eq!(inet_oid, oid::INET);
807 assert_eq!(inet_format, 0);
808
809 let cidr = Cidr::new("10.0.0.0/8");
810 let (_, cidr_oid, cidr_format) = cidr.to_pg();
811 assert_eq!(cidr_oid, oid::CIDR);
812 assert_eq!(cidr_format, 0);
813
814 let mac = MacAddr::new("08:00:2b:01:02:03");
815 let (_, mac_oid, mac_format) = mac.to_pg();
816 assert_eq!(mac_oid, oid::MACADDR);
817 assert_eq!(mac_format, 0);
818 }
819
820 #[test]
821 fn test_json_from_pg_honors_oid_and_format() {
822 let json = Json::from_pg(br#"{"ok":true}"#, oid::JSON, 0).unwrap();
823 assert_eq!(json.0, r#"{"ok":true}"#);
824
825 let jsonb_text = Json::from_pg(br#"{"ok":true}"#, oid::JSONB, 0).unwrap();
826 assert_eq!(jsonb_text.0, r#"{"ok":true}"#);
827
828 let jsonb_binary = Json::from_pg(&[1, b'{', b'}'], oid::JSONB, 1).unwrap();
829 assert_eq!(jsonb_binary.0, "{}");
830
831 assert!(Json::from_pg(&[], oid::JSONB, 1).is_err());
832 assert!(Json::from_pg(b"42", oid::INT4, 0).is_err());
833 }
834}