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: 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 if bytes.len() != 1 {
174 return Err(TypeError::InvalidData(
175 "Expected 1 byte for boolean".to_string(),
176 ));
177 }
178 match bytes[0] {
179 0 => Ok(false),
180 1 => Ok(true),
181 value => Err(TypeError::InvalidData(format!(
182 "Invalid binary boolean: {}",
183 value
184 ))),
185 }
186 } else {
187 match std::str::from_utf8(bytes)
189 .map_err(|e| TypeError::InvalidData(e.to_string()))?
190 .trim()
191 {
192 "t" | "T" | "true" | "TRUE" | "1" => Ok(true),
193 "f" | "F" | "false" | "FALSE" | "0" => Ok(false),
194 _ => Err(TypeError::InvalidData("Invalid boolean".to_string())),
195 }
196 }
197 }
198}
199
200impl ToPg for bool {
201 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
202 (vec![if *self { 1 } else { 0 }], oid::BOOL, 1)
203 }
204}
205
206#[derive(Debug, Clone, PartialEq)]
210pub struct Uuid(pub String);
211
212impl FromPg for Uuid {
213 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
214 if oid_val != oid::UUID {
215 return Err(TypeError::UnexpectedOid {
216 expected: "uuid",
217 got: oid_val,
218 });
219 }
220
221 if format == 1 {
222 if bytes.len() != 16 {
223 return Err(TypeError::InvalidData(
224 "Expected 16 bytes for UUID".to_string(),
225 ));
226 }
227 decode_uuid(bytes).map(Uuid).map_err(TypeError::InvalidData)
228 } else {
229 std::str::from_utf8(bytes)
231 .map(str::to_owned)
232 .map(Uuid)
233 .map_err(|e| TypeError::InvalidData(e.to_string()))
234 }
235 }
236}
237
238impl ToPg for Uuid {
239 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
240 (self.0.as_bytes().to_vec(), oid::UUID, 0)
242 }
243}
244
245#[cfg(feature = "uuid")]
246impl FromPg for uuid::Uuid {
247 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
248 let wrapped = Uuid::from_pg(bytes, oid_val, format)?;
249 uuid::Uuid::parse_str(&wrapped.0)
250 .map_err(|e| TypeError::InvalidData(format!("Invalid UUID: {}", e)))
251 }
252}
253
254#[cfg(feature = "uuid")]
255impl ToPg for uuid::Uuid {
256 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
257 (self.as_bytes().to_vec(), oid::UUID, 1)
258 }
259}
260
261fn from_utf8_string(bytes: &[u8]) -> Result<String, TypeError> {
264 std::str::from_utf8(bytes)
265 .map(|s| s.to_string())
266 .map_err(|e| TypeError::InvalidData(e.to_string()))
267}
268
269fn decode_inet_like_binary(bytes: &[u8], force_prefix: bool) -> Result<String, TypeError> {
270 if bytes.len() < 4 {
277 return Err(TypeError::InvalidData(
278 "inet/cidr binary payload too short".to_string(),
279 ));
280 }
281
282 let family = bytes[0];
283 let bits = bytes[1];
284 let is_cidr = bytes[2];
285 let addr_len = bytes[3] as usize;
286
287 if bytes.len() != 4 + addr_len {
288 return Err(TypeError::InvalidData(
289 "inet/cidr binary payload length mismatch".to_string(),
290 ));
291 }
292 if is_cidr > 1 {
293 return Err(TypeError::InvalidData(
294 "invalid inet/cidr is_cidr flag".to_string(),
295 ));
296 }
297
298 let addr = &bytes[4..];
299 match family {
300 2 => {
301 if addr_len != 4 {
302 return Err(TypeError::InvalidData(
303 "invalid IPv4 inet/cidr address length".to_string(),
304 ));
305 }
306 if bits > 32 {
307 return Err(TypeError::InvalidData(
308 "invalid IPv4 inet/cidr prefix length".to_string(),
309 ));
310 }
311 let ip = std::net::Ipv4Addr::from([addr[0], addr[1], addr[2], addr[3]]);
312 let include_prefix = force_prefix || is_cidr != 0 || bits != 32;
313 if include_prefix {
314 Ok(format!("{}/{}", ip, bits))
315 } else {
316 Ok(ip.to_string())
317 }
318 }
319 3 => {
320 if addr_len != 16 {
321 return Err(TypeError::InvalidData(
322 "invalid IPv6 inet/cidr address length".to_string(),
323 ));
324 }
325 if bits > 128 {
326 return Err(TypeError::InvalidData(
327 "invalid IPv6 inet/cidr prefix length".to_string(),
328 ));
329 }
330 let mut full = [0u8; 16];
331 full.copy_from_slice(addr);
332 let ip = std::net::Ipv6Addr::from(full);
333 let include_prefix = force_prefix || is_cidr != 0 || bits != 128;
334 if include_prefix {
335 Ok(format!("{}/{}", ip, bits))
336 } else {
337 Ok(ip.to_string())
338 }
339 }
340 _ => Err(TypeError::InvalidData(format!(
341 "unsupported inet/cidr address family: {}",
342 family
343 ))),
344 }
345}
346
347#[derive(Debug, Clone, PartialEq, Eq)]
349pub struct Inet(pub String);
350
351impl Inet {
352 pub fn new(s: impl Into<String>) -> Self {
354 Self(s.into())
355 }
356
357 pub fn as_str(&self) -> &str {
359 &self.0
360 }
361}
362
363impl FromPg for Inet {
364 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
365 if oid_val != oid::INET {
366 return Err(TypeError::UnexpectedOid {
367 expected: "inet",
368 got: oid_val,
369 });
370 }
371
372 let s = if format == 1 {
373 decode_inet_like_binary(bytes, false)?
374 } else {
375 from_utf8_string(bytes)?
376 };
377 Ok(Inet(s))
378 }
379}
380
381impl ToPg for Inet {
382 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
383 (self.0.as_bytes().to_vec(), oid::INET, 0)
384 }
385}
386
387#[derive(Debug, Clone, PartialEq, Eq)]
389pub struct Cidr(pub String);
390
391impl Cidr {
392 pub fn new(s: impl Into<String>) -> Self {
394 Self(s.into())
395 }
396
397 pub fn as_str(&self) -> &str {
399 &self.0
400 }
401}
402
403impl FromPg for Cidr {
404 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
405 if oid_val != oid::CIDR {
406 return Err(TypeError::UnexpectedOid {
407 expected: "cidr",
408 got: oid_val,
409 });
410 }
411
412 let s = if format == 1 {
413 decode_inet_like_binary(bytes, true)?
414 } else {
415 from_utf8_string(bytes)?
416 };
417 Ok(Cidr(s))
418 }
419}
420
421impl ToPg for Cidr {
422 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
423 (self.0.as_bytes().to_vec(), oid::CIDR, 0)
424 }
425}
426
427#[derive(Debug, Clone, PartialEq, Eq)]
429pub struct MacAddr(pub String);
430
431impl MacAddr {
432 pub fn new(s: impl Into<String>) -> Self {
434 Self(s.into())
435 }
436
437 pub fn as_str(&self) -> &str {
439 &self.0
440 }
441}
442
443impl FromPg for MacAddr {
444 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
445 if oid_val != oid::MACADDR {
446 return Err(TypeError::UnexpectedOid {
447 expected: "macaddr",
448 got: oid_val,
449 });
450 }
451
452 let s = if format == 1 {
453 if bytes.len() != 6 {
454 return Err(TypeError::InvalidData(
455 "Expected 6 bytes for macaddr".to_string(),
456 ));
457 }
458 format!(
459 "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
460 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
461 )
462 } else {
463 from_utf8_string(bytes)?
464 };
465
466 Ok(MacAddr(s))
467 }
468}
469
470impl ToPg for MacAddr {
471 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
472 (self.0.as_bytes().to_vec(), oid::MACADDR, 0)
473 }
474}
475
476#[derive(Debug, Clone, PartialEq)]
480pub struct Json(pub String);
481
482impl FromPg for Json {
483 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
484 let json_str = match (oid_val, format) {
485 (oid::JSON, _) | (oid::JSONB, 0) => {
486 decode_json(bytes).map_err(TypeError::InvalidData)?
487 }
488 (oid::JSONB, 1) => decode_jsonb(bytes).map_err(TypeError::InvalidData)?,
489 (oid::JSONB, other) => {
490 return Err(TypeError::InvalidData(format!(
491 "Unsupported JSONB format code: {}",
492 other
493 )));
494 }
495 _ => {
496 return Err(TypeError::UnexpectedOid {
497 expected: "json/jsonb",
498 got: oid_val,
499 });
500 }
501 };
502 Ok(Json(json_str))
503 }
504}
505
506impl ToPg for Json {
507 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
508 let mut buf = Vec::with_capacity(1 + self.0.len());
510 buf.push(1); buf.extend_from_slice(self.0.as_bytes());
512 (buf, oid::JSONB, 1)
513 }
514}
515
516impl FromPg for Vec<String> {
519 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
520 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
521 try_decode_text_array(s).map_err(TypeError::InvalidData)
522 }
523}
524
525impl FromPg for Vec<i64> {
526 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
527 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
528 crate::protocol::types::decode_int_array(s).map_err(TypeError::InvalidData)
529 }
530}
531
532impl<T: FromPg> FromPg for Option<T> {
535 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
536 Ok(Some(T::from_pg(bytes, oid_val, format)?))
538 }
539}
540
541impl FromPg for Vec<u8> {
544 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
545 Ok(bytes.to_vec())
546 }
547}
548
549impl ToPg for Vec<u8> {
550 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
551 (self.clone(), oid::BYTEA, 1)
552 }
553}
554
555impl ToPg for &[u8] {
556 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
557 (self.to_vec(), oid::BYTEA, 1)
558 }
559}
560
561#[cfg(test)]
562mod tests {
563 use super::*;
564
565 #[test]
566 fn test_string_from_pg() {
567 let result = String::from_pg(b"hello", oid::TEXT, 0).unwrap();
568 assert_eq!(result, "hello");
569 }
570
571 #[test]
572 fn test_i32_from_pg_text() {
573 let result = i32::from_pg(b"42", oid::INT4, 0).unwrap();
574 assert_eq!(result, 42);
575 }
576
577 #[test]
578 fn test_i32_from_pg_binary() {
579 let bytes = 42i32.to_be_bytes();
580 let result = i32::from_pg(&bytes, oid::INT4, 1).unwrap();
581 assert_eq!(result, 42);
582 }
583
584 #[test]
585 fn test_bool_from_pg() {
586 assert!(bool::from_pg(b"t", oid::BOOL, 0).unwrap());
587 assert!(!bool::from_pg(b"f", oid::BOOL, 0).unwrap());
588 assert!(bool::from_pg(&[1], oid::BOOL, 1).unwrap());
589 assert!(!bool::from_pg(&[0], oid::BOOL, 1).unwrap());
590 }
591
592 #[test]
593 fn test_bool_from_pg_rejects_malformed_values() {
594 assert!(bool::from_pg(&[], oid::BOOL, 1).is_err());
595 assert!(bool::from_pg(&[2], oid::BOOL, 1).is_err());
596 assert!(bool::from_pg(b"trash", oid::BOOL, 0).is_err());
597 assert!(bool::from_pg(b"falsey", oid::BOOL, 0).is_err());
598 }
599
600 #[test]
601 fn test_uuid_from_pg_binary() {
602 let uuid_bytes: [u8; 16] = [
603 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
604 0x00, 0x00,
605 ];
606 let result = Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
607 assert_eq!(result.0, "550e8400-e29b-41d4-a716-446655440000");
608 }
609
610 #[test]
611 fn test_uuid_from_pg_binary_rejects_bad_length() {
612 let err = Uuid::from_pg(b"550e8400-e29b-41d4-a716-446655440000", oid::UUID, 1).unwrap_err();
613
614 assert!(matches!(err, TypeError::InvalidData(msg) if msg.contains("16 bytes")));
615 }
616
617 #[cfg(feature = "uuid")]
618 #[test]
619 fn test_std_uuid_from_pg_binary() {
620 let uuid_bytes: [u8; 16] = [
621 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
622 0x00, 0x00,
623 ];
624 let result = uuid::Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
625 assert_eq!(result.to_string(), "550e8400-e29b-41d4-a716-446655440000");
626 }
627
628 #[test]
629 fn test_inet_from_pg_text() {
630 let inet = Inet::from_pg(b"10.0.0.1", oid::INET, 0).unwrap();
631 assert_eq!(inet.0, "10.0.0.1");
632 }
633
634 #[test]
635 fn test_inet_from_pg_binary_ipv4() {
636 let bytes = [2u8, 32, 0, 4, 10, 1, 2, 3];
638 let inet = Inet::from_pg(&bytes, oid::INET, 1).unwrap();
639 assert_eq!(inet.0, "10.1.2.3");
640 }
641
642 #[test]
643 fn test_cidr_from_pg_binary_ipv4() {
644 let bytes = [2u8, 24, 1, 4, 192, 168, 1, 0];
646 let cidr = Cidr::from_pg(&bytes, oid::CIDR, 1).unwrap();
647 assert_eq!(cidr.0, "192.168.1.0/24");
648 }
649
650 #[test]
651 fn test_network_types_reject_malformed_binary_payloads() {
652 assert!(Inet::from_pg(&[2u8, 33, 0, 4, 10, 1, 2, 3], oid::INET, 1).is_err());
653 assert!(Inet::from_pg(&[2u8, 32, 0, 1, 10], oid::INET, 1).is_err());
654 assert!(Inet::from_pg(&[2u8, 32, 2, 4, 10, 1, 2, 3], oid::INET, 1).is_err());
655
656 let mut ipv6 = vec![3u8, 129, 0, 16];
657 ipv6.extend_from_slice(&[0u8; 16]);
658 assert!(Inet::from_pg(&ipv6, oid::INET, 1).is_err());
659 }
660
661 #[test]
662 fn test_macaddr_from_pg_binary() {
663 let bytes = [0x08u8, 0x00, 0x2b, 0x01, 0x02, 0x03];
664 let mac = MacAddr::from_pg(&bytes, oid::MACADDR, 1).unwrap();
665 assert_eq!(mac.0, "08:00:2b:01:02:03");
666 }
667
668 #[test]
669 fn test_network_types_to_pg_oids() {
670 let inet = Inet::new("10.0.0.0/8");
671 let (_, inet_oid, inet_format) = inet.to_pg();
672 assert_eq!(inet_oid, oid::INET);
673 assert_eq!(inet_format, 0);
674
675 let cidr = Cidr::new("10.0.0.0/8");
676 let (_, cidr_oid, cidr_format) = cidr.to_pg();
677 assert_eq!(cidr_oid, oid::CIDR);
678 assert_eq!(cidr_format, 0);
679
680 let mac = MacAddr::new("08:00:2b:01:02:03");
681 let (_, mac_oid, mac_format) = mac.to_pg();
682 assert_eq!(mac_oid, oid::MACADDR);
683 assert_eq!(mac_format, 0);
684 }
685
686 #[test]
687 fn test_json_from_pg_honors_oid_and_format() {
688 let json = Json::from_pg(br#"{"ok":true}"#, oid::JSON, 0).unwrap();
689 assert_eq!(json.0, r#"{"ok":true}"#);
690
691 let jsonb_text = Json::from_pg(br#"{"ok":true}"#, oid::JSONB, 0).unwrap();
692 assert_eq!(jsonb_text.0, r#"{"ok":true}"#);
693
694 let jsonb_binary = Json::from_pg(&[1, b'{', b'}'], oid::JSONB, 1).unwrap();
695 assert_eq!(jsonb_binary.0, "{}");
696
697 assert!(Json::from_pg(&[], oid::JSONB, 1).is_err());
698 assert!(Json::from_pg(b"42", oid::INT4, 0).is_err());
699 }
700}