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