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 { expected: &'static str, got: u32 },
18 InvalidData(String),
20 UnexpectedNull,
22}
23
24impl std::fmt::Display for TypeError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 TypeError::UnexpectedOid { expected, got } => {
28 write!(f, "Expected {} type, got OID {}", expected, got)
29 }
30 TypeError::InvalidData(msg) => write!(f, "Invalid data: {}", msg),
31 TypeError::UnexpectedNull => write!(f, "Unexpected NULL value"),
32 }
33 }
34}
35
36impl std::error::Error for TypeError {}
37
38pub trait FromPg: Sized {
40 fn from_pg(bytes: &[u8], oid: u32, format: i16) -> Result<Self, TypeError>;
47}
48
49pub trait ToPg {
51 fn to_pg(&self) -> (Vec<u8>, u32, i16);
54}
55
56impl FromPg for String {
59 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
60 String::from_utf8(bytes.to_vec())
61 .map_err(|e| TypeError::InvalidData(format!("Invalid UTF-8: {}", e)))
62 }
63}
64
65impl ToPg for String {
66 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
67 (self.as_bytes().to_vec(), oid::TEXT, 0)
68 }
69}
70
71impl ToPg for &str {
72 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
73 (self.as_bytes().to_vec(), oid::TEXT, 0)
74 }
75}
76
77impl FromPg for i32 {
80 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
81 if format == 1 {
82 if bytes.len() != 4 {
84 return Err(TypeError::InvalidData(
85 "Expected 4 bytes for i32".to_string(),
86 ));
87 }
88 Ok(i32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
89 } else {
90 std::str::from_utf8(bytes)
92 .map_err(|e| TypeError::InvalidData(e.to_string()))?
93 .parse()
94 .map_err(|e| TypeError::InvalidData(format!("Invalid i32: {}", e)))
95 }
96 }
97}
98
99impl ToPg for i32 {
100 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
101 (self.to_be_bytes().to_vec(), oid::INT4, 1)
102 }
103}
104
105impl FromPg for i64 {
106 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
107 if format == 1 {
108 if bytes.len() != 8 {
110 return Err(TypeError::InvalidData(
111 "Expected 8 bytes for i64".to_string(),
112 ));
113 }
114 Ok(i64::from_be_bytes([
115 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
116 ]))
117 } else {
118 std::str::from_utf8(bytes)
120 .map_err(|e| TypeError::InvalidData(e.to_string()))?
121 .parse()
122 .map_err(|e| TypeError::InvalidData(format!("Invalid i64: {}", e)))
123 }
124 }
125}
126
127impl ToPg for i64 {
128 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
129 (self.to_be_bytes().to_vec(), oid::INT8, 1)
130 }
131}
132
133impl FromPg for f64 {
136 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
137 if format == 1 {
138 if bytes.len() != 8 {
140 return Err(TypeError::InvalidData(
141 "Expected 8 bytes for f64".to_string(),
142 ));
143 }
144 Ok(f64::from_be_bytes([
145 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
146 ]))
147 } else {
148 std::str::from_utf8(bytes)
150 .map_err(|e| TypeError::InvalidData(e.to_string()))?
151 .parse()
152 .map_err(|e| TypeError::InvalidData(format!("Invalid f64: {}", e)))
153 }
154 }
155}
156
157impl ToPg for f64 {
158 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
159 (self.to_be_bytes().to_vec(), oid::FLOAT8, 1)
160 }
161}
162
163impl FromPg for bool {
166 fn from_pg(bytes: &[u8], _oid: u32, format: i16) -> Result<Self, TypeError> {
167 if format == 1 {
168 Ok(bytes.first().map(|b| *b != 0).unwrap_or(false))
170 } else {
171 match bytes.first() {
173 Some(b't') | Some(b'T') | Some(b'1') => Ok(true),
174 Some(b'f') | Some(b'F') | Some(b'0') => Ok(false),
175 _ => Err(TypeError::InvalidData("Invalid boolean".to_string())),
176 }
177 }
178 }
179}
180
181impl ToPg for bool {
182 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
183 (vec![if *self { 1 } else { 0 }], oid::BOOL, 1)
184 }
185}
186
187#[derive(Debug, Clone, PartialEq)]
191pub struct Uuid(pub String);
192
193impl FromPg for Uuid {
194 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
195 if oid_val != oid::UUID {
196 return Err(TypeError::UnexpectedOid {
197 expected: "uuid",
198 got: oid_val,
199 });
200 }
201
202 if format == 1 && bytes.len() == 16 {
203 decode_uuid(bytes).map(Uuid).map_err(TypeError::InvalidData)
205 } else {
206 String::from_utf8(bytes.to_vec())
208 .map(Uuid)
209 .map_err(|e| TypeError::InvalidData(e.to_string()))
210 }
211 }
212}
213
214impl ToPg for Uuid {
215 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
216 (self.0.as_bytes().to_vec(), oid::UUID, 0)
218 }
219}
220
221#[derive(Debug, Clone, PartialEq)]
225pub struct Json(pub String);
226
227impl FromPg for Json {
228 fn from_pg(bytes: &[u8], oid_val: u32, _format: i16) -> Result<Self, TypeError> {
229 let json_str = if oid_val == oid::JSONB {
230 decode_jsonb(bytes).map_err(TypeError::InvalidData)?
231 } else {
232 decode_json(bytes).map_err(TypeError::InvalidData)?
233 };
234 Ok(Json(json_str))
235 }
236}
237
238impl ToPg for Json {
239 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
240 let mut buf = Vec::with_capacity(1 + self.0.len());
242 buf.push(1); buf.extend_from_slice(self.0.as_bytes());
244 (buf, oid::JSONB, 1)
245 }
246}
247
248impl FromPg for Vec<String> {
251 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
252 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
253 Ok(decode_text_array(s))
254 }
255}
256
257impl FromPg for Vec<i64> {
258 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
259 let s = std::str::from_utf8(bytes).map_err(|e| TypeError::InvalidData(e.to_string()))?;
260 crate::protocol::types::decode_int_array(s).map_err(TypeError::InvalidData)
261 }
262}
263
264impl<T: FromPg> FromPg for Option<T> {
267 fn from_pg(bytes: &[u8], oid_val: u32, format: i16) -> Result<Self, TypeError> {
268 Ok(Some(T::from_pg(bytes, oid_val, format)?))
270 }
271}
272
273impl FromPg for Vec<u8> {
276 fn from_pg(bytes: &[u8], _oid: u32, _format: i16) -> Result<Self, TypeError> {
277 Ok(bytes.to_vec())
278 }
279}
280
281impl ToPg for Vec<u8> {
282 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
283 (self.clone(), oid::BYTEA, 1)
284 }
285}
286
287impl ToPg for &[u8] {
288 fn to_pg(&self) -> (Vec<u8>, u32, i16) {
289 (self.to_vec(), oid::BYTEA, 1)
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::*;
296
297 #[test]
298 fn test_string_from_pg() {
299 let result = String::from_pg(b"hello", oid::TEXT, 0).unwrap();
300 assert_eq!(result, "hello");
301 }
302
303 #[test]
304 fn test_i32_from_pg_text() {
305 let result = i32::from_pg(b"42", oid::INT4, 0).unwrap();
306 assert_eq!(result, 42);
307 }
308
309 #[test]
310 fn test_i32_from_pg_binary() {
311 let bytes = 42i32.to_be_bytes();
312 let result = i32::from_pg(&bytes, oid::INT4, 1).unwrap();
313 assert_eq!(result, 42);
314 }
315
316 #[test]
317 fn test_bool_from_pg() {
318 assert!(bool::from_pg(b"t", oid::BOOL, 0).unwrap());
319 assert!(!bool::from_pg(b"f", oid::BOOL, 0).unwrap());
320 assert!(bool::from_pg(&[1], oid::BOOL, 1).unwrap());
321 assert!(!bool::from_pg(&[0], oid::BOOL, 1).unwrap());
322 }
323
324 #[test]
325 fn test_uuid_from_pg_binary() {
326 let uuid_bytes: [u8; 16] = [
327 0x55, 0x0e, 0x84, 0x00, 0xe2, 0x9b, 0x41, 0xd4, 0xa7, 0x16, 0x44, 0x66, 0x55, 0x44,
328 0x00, 0x00,
329 ];
330 let result = Uuid::from_pg(&uuid_bytes, oid::UUID, 1).unwrap();
331 assert_eq!(result.0, "550e8400-e29b-41d4-a716-446655440000");
332 }
333}